diff --git a/docs/kratos/passwordless/05_passkeys.mdx b/docs/kratos/passwordless/05_passkeys.mdx
index 79c3c3674..6d2dcad5c 100644
--- a/docs/kratos/passwordless/05_passkeys.mdx
+++ b/docs/kratos/passwordless/05_passkeys.mdx
@@ -148,6 +148,18 @@ is defined, the passkey strategy will not work.
}
```
+### Implementing passkeys in your application
+
+After configuring passkeys, you need to integrate them into your application. The implementation differs depending on your
+platform:
+
+- Web applications can use Ory's `webauthn.js` helper script or manually integrate the WebAuthn API.
+- Mobile applications (iOS and Android) require platform-specific credential management APIs and direct integration with Ory's
+ JSON API endpoints.
+
+For detailed implementation instructions, code examples, and best practices for both web and mobile platforms, see
+[Implementing passkeys for web and mobile](./06_passkeys-mobile.mdx).
+
## Passkeys with the WebAuthN strategy
### Configuration
diff --git a/docs/kratos/passwordless/06_passkeys-mobile.mdx b/docs/kratos/passwordless/06_passkeys-mobile.mdx
new file mode 100644
index 000000000..2f5533939
--- /dev/null
+++ b/docs/kratos/passwordless/06_passkeys-mobile.mdx
@@ -0,0 +1,903 @@
+---
+id: passkeys-mobile
+title: Implement passkey authentication in web and mobile applications
+sidebar_label: Passkeys for mobile
+slug: passkeys-mobile-web-implementation
+---
+
+import Tabs from "@theme/Tabs"
+import TabItem from "@theme/TabItem"
+
+This guide covers how to implement passkey authentication in your applications using Ory Kratos or Ory Network. Passkeys provide a
+passwordless authentication experience using WebAuthn across web browsers and mobile platforms.
+
+This page assumes you have already configured the passkey method in your Ory configuration. See the
+[Passkeys overview](./05_passkeys.mdx) for initial setup instructions.
+
+:::note
+
+Code examples in this guide are illustrative and likely need adjustments based on your specific configuration, identity schema,
+and application requirements.
+
+:::
+
+## Overview
+
+Passkey implementation differs between platforms:
+
+- Web applications use browser-native WebAuthn APIs with JavaScript.
+- Mobile applications use platform-specific credential management APIs (iOS AuthenticationServices, Android CredentialManager)
+ with Ory's JSON API endpoints.
+
+This guide focuses on the integration patterns for each platform.
+
+## Web implementation
+
+For web applications, you can use the browser's native WebAuthn API to create and authenticate with passkeys.
+
+### Using Ory's webauthn.js
+
+Ory provides a `webauthn.js` helper script that simplifies WebAuthn integration in browser flows. When you initialize a
+registration or login flow through the browser, Ory automatically injects the necessary JavaScript to handle passkey operations.
+
+```html
+
+
+```
+
+The script automatically:
+
+- Detects passkey-related form fields.
+- Calls `navigator.credentials.create()` for registration.
+- Calls `navigator.credentials.get()` for authentication.
+- Submits the WebAuthn response back to Ory.
+
+See [Custom UI Advanced Integration](../bring-your-own-ui/custom-ui-advanced-integration#passwordless-authentication.mdx) for
+details on using `webauthn.js` in custom UIs.
+
+### Manual WebAuthn integration
+
+For more control, you can manually integrate the [W3C WebAuthn API](https://www.w3.org/TR/webauthn-2/):
+
+1. Initialize a registration or login flow via Ory's API.
+2. Parse the WebAuthn challenge from the flow response.
+3. Call `navigator.credentials.create()` or `navigator.credentials.get()`.
+4. Submit the credential response back to Ory.
+
+The WebAuthn API is well-documented by the W3C and MDN:
+
+- [W3C WebAuthn Specification](https://www.w3.org/TR/webauthn-2/)
+- [MDN Web Authentication API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API)
+
+## Mobile implementation
+
+Mobile passkey implementation requires using platform-specific APIs and Ory's JSON API endpoints. Unlike browser flows, mobile
+apps don't receive the `webauthn.js` script and must handle credential operations manually.
+
+### Platform requirements
+
+Both iOS and Android require configuration to associate your app with your authentication domain.
+
+### iOS Associated Domains
+
+iOS requires an Associated Domains entitlement that links your app to your authentication domain.
+
+#### Add the Associated Domains entitlement
+
+Add the entitlement to your Xcode project:
+
+```xml
+
+
+
+
+
+ com.apple.developer.associated-domains
+
+ webcredentials:$PROJECT_SLUG.projects.oryapis.com
+
+
+
+```
+
+The domain in your entitlement must match the Relying Party ID in your Kratos passkey configuration.
+
+#### Serve the apple-app-site-association file
+
+You need to host an `apple-app-site-association` file at `https://{your_domain}/.well-known/apple-app-site-association` to allow
+the application to register and authenticate with credentials associated with the Relying Party ID.
+
+Example `apple-app-site-association` file:
+
+```json
+{
+ "webcredentials": {
+ "apps": ["ABCDE12345.com.example.app"]
+ }
+}
+```
+
+The value uses the format `{Application_Identifier_Prefix}.{Bundle_Identifier}`. Find your Application Identifier Prefix (Team ID)
+in the [Apple Developer portal](https://developer.apple.com/account) under Membership.
+
+:::warning
+
+Ory Network doesn't currently host `apple-app-site-association` files automatically.
+
+:::
+
+```mdx-code-block
+
+
+```
+
+You must host this file on your own domain and configure your Kratos passkey settings to use that domain as the Relying Party ID.
+
+```mdx-code-block
+
+
+```
+
+For self-hosted Kratos, serve the `apple-app-site-association` file at your authentication domain:
+
+`https://ory.your-custom-domain.com/.well-known/apple-app-site-association`
+
+```mdx-code-block
+
+
+```
+
+#### Important constraints
+
+The domain in your Associated Domains entitlement must exactly match the `rp.id` in your Kratos passkey configuration. The domain
+must be accessible via HTTPS with a valid TLS certificate. The `apple-app-site-association` file must be served with
+`Content-Type: application/json`.
+
+#### Apple documentation
+
+- [Supporting Associated Domains](https://developer.apple.com/documentation/xcode/supporting-associated-domains)
+- [About the apple-app-site-association file](https://developer.apple.com/documentation/bundleresources/applinks/details/supporting_associated_domains)
+
+### Android Digital Asset Links
+
+Android requires an `assetlinks.json` file to verify your app's relationship with your authentication domain.
+
+#### Serve the assetlinks.json file
+
+You need to host an `assetlinks.json` file at `https://{your_domain}/.well-known/assetlinks.json` to verify your app's
+relationship with your authentication domain.
+
+Example `assetlinks.json` file:
+
+```json
+[
+ {
+ "relation": ["delegate_permission/common.handle_all_urls", "delegate_permission/common.get_login_creds"],
+ "target": {
+ "namespace": "android_app",
+ "package_name": "com.example.yourapp",
+ "sha256_cert_fingerprints": [
+ "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
+ ]
+ }
+ }
+]
+```
+
+Generate your SHA-256 certificate fingerprint:
+
+```shell
+keytool -list -v -keystore your-keystore.jks
+```
+
+:::warning Ory Network doesn't currently host `assetlinks.json` files automatically. This feature is planned for future releases.
+:::
+
+```mdx-code-block
+
+
+```
+
+You must host this file on your own domain and configure your Kratos passkey settings to use that domain as the Relying Party ID.
+
+```mdx-code-block
+
+
+```
+
+For self-hosted Kratos, serve the `assetlinks.json` file at your authentication domain:
+
+`https://ory.your-custom-domain.com/.well-known/assetlinks.json`
+
+```mdx-code-block
+
+
+```
+
+#### Important constraints
+
+The domain in your `assetlinks.json` must exactly match the `rp.id` in your Kratos passkey configuration. The domain must be
+accessible via HTTPS with a valid TLS certificate. The file must be served with `Content-Type: application/json`. The package name
+must match your Android app's `applicationId`. The SHA-256 fingerprint must match your app's signing key.
+
+#### Android documentation
+
+- [Android Credential Manager](https://developer.android.com/training/sign-in/passkeys)
+- [Digital Asset Links](https://developers.google.com/digital-asset-links/v1/getting-started)
+- [Verify Android App Links](https://developer.android.com/training/app-links/verify-android-applinks)
+
+### iOS implementation
+
+iOS passkey support uses the [AuthenticationServices framework](https://developer.apple.com/documentation/authenticationservices).
+Here's how to integrate with Ory's API.
+
+#### Registration flow
+
+Initialize the registration flow:
+
+```swift
+func initializeRegistrationFlow() async throws -> FlowResponse {
+ var request = URLRequest(url: URL(string: "\(oryBaseURL)/self-service/registration/api")!)
+ request.httpMethod = "GET"
+ request.setValue("application/json", forHTTPHeaderField: "Accept")
+
+ let (data, response) = try await URLSession.shared.data(for: request)
+ // Handle response...
+ return try decodeFlowResponse(from: data)
+}
+```
+
+Parse the WebAuthn challenge by extracting the challenge from the `passkey_create_data` node:
+
+```swift
+func extractRegistrationChallenge(_ flow: FlowResponse) -> String? {
+ guard let ui = flow.raw["ui"] as? [String: Any],
+ let nodes = ui["nodes"] as? [[String: Any]] else {
+ return nil
+ }
+
+ // Find the passkey_create_data node
+ for node in nodes {
+ guard let attributes = node["attributes"] as? [String: Any],
+ let name = attributes["name"] as? String,
+ name == "passkey_create_data" else {
+ continue
+ }
+
+ // Parse the nested JSON value
+ if let valueStr = attributes["value"] as? String,
+ let valueData = valueStr.data(using: .utf8),
+ let json = try? JSONSerialization.jsonObject(with: valueData) as? [String: Any],
+ let credentialOptions = json["credentialOptions"] as? [String: Any],
+ let publicKey = credentialOptions["publicKey"] as? [String: Any],
+ let challenge = publicKey["challenge"] as? String {
+ return challenge
+ }
+ }
+ return nil
+}
+```
+
+The `passkey_create_data` node contains a JSON string with this structure:
+
+```json
+{
+ "credentialOptions": {
+ "publicKey": {
+ "challenge": "base64url-encoded-challenge",
+ "rp": { "name": "Your App", "id": "ory.your-custom-domain.com" },
+ "user": { "id": "base64url-user-id", "name": "", "displayName": "" },
+ "pubKeyCredParams": [
+ { "type": "public-key", "alg": -7 },
+ { "type": "public-key", "alg": -257 }
+ ],
+ "authenticatorSelection": {
+ "userVerification": "required",
+ "residentKey": "required"
+ }
+ }
+ },
+ "displayNameFieldName": "traits.email"
+}
+```
+
+Create the passkey:
+
+```swift
+func signUpWith(userName: String, challenge: Data, domain: String) {
+ let publicKeyCredentialProvider = ASAuthorizationPlatformPublicKeyCredentialProvider(
+ relyingPartyIdentifier: domain
+ )
+
+ let userID = Data(UUID().uuidString.utf8)
+ let registrationRequest = publicKeyCredentialProvider.createCredentialRegistrationRequest(
+ challenge: challenge,
+ name: userName,
+ userID: userID
+ )
+
+ let authController = ASAuthorizationController(authorizationRequests: [registrationRequest])
+ authController.delegate = self
+ authController.presentationContextProvider = self
+ authController.performRequests()
+}
+```
+
+When the user completes registration, format and submit the credential:
+
+```swift
+func submitRegistration(credential: ASAuthorizationPlatformPublicKeyCredentialRegistration) async throws {
+ let credentialDict: [String: Any] = [
+ "id": credential.credentialID.base64URLEncodedString(),
+ "rawId": credential.credentialID.base64URLEncodedString(),
+ "type": "public-key",
+ "response": [
+ "clientDataJSON": credential.rawClientDataJSON.base64URLEncodedString(),
+ "attestationObject": credential.rawAttestationObject?.base64URLEncodedString() ?? ""
+ ]
+ ]
+
+ let credentialJSON = try JSONSerialization.data(withJSONObject: credentialDict)
+ let credentialString = String(data: credentialJSON, encoding: .utf8)!
+
+ var request = URLRequest(url: URL(string: "\(oryBaseURL)/self-service/registration?flow=\(flowId)")!)
+ request.httpMethod = "POST"
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
+
+ let payload: [String: Any] = [
+ "method": "passkey",
+ "passkey_register": credentialString,
+ "traits": [
+ "email": userName // Or other identity traits
+ ]
+ ]
+
+ request.httpBody = try JSONSerialization.data(withJSONObject: payload)
+ let (data, response) = try await URLSession.shared.data(for: request)
+ // Handle response...
+}
+```
+
+#### Login flow
+
+Initialize the login flow:
+
+```swift
+func initializeLoginFlow() async throws -> FlowResponse {
+ var request = URLRequest(url: URL(string: "\(oryBaseURL)/self-service/login/api")!)
+ request.httpMethod = "GET"
+ request.setValue("application/json", forHTTPHeaderField: "Accept")
+
+ let (data, response) = try await URLSession.shared.data(for: request)
+ return try decodeFlowResponse(from: data)
+}
+```
+
+Extract the challenge from the `passkey_challenge` node:
+
+```swift
+func extractLoginChallenge(_ flow: FlowResponse) -> String? {
+ guard let ui = flow.raw["ui"] as? [String: Any],
+ let nodes = ui["nodes"] as? [[String: Any]] else {
+ return nil
+ }
+
+ // Find the passkey_challenge node
+ for node in nodes {
+ guard let attributes = node["attributes"] as? [String: Any],
+ let name = attributes["name"] as? String,
+ name == "passkey_challenge" else {
+ continue
+ }
+
+ // Parse the JSON value
+ if let valueStr = attributes["value"] as? String,
+ let valueData = valueStr.data(using: .utf8),
+ let json = try? JSONSerialization.jsonObject(with: valueData) as? [String: Any],
+ let publicKey = json["publicKey"] as? [String: Any],
+ let challenge = publicKey["challenge"] as? String {
+ return challenge
+ }
+ }
+ return nil
+}
+```
+
+The `passkey_challenge` node contains a JSON string with this structure:
+
+```json
+{
+ "publicKey": {
+ "challenge": "base64url-encoded-challenge",
+ "rpId": "ory.your-custom-domain.com",
+ "allowCredentials": [],
+ "userVerification": "required"
+ }
+}
+```
+
+Authenticate with passkey:
+
+```swift
+func signInWith(challenge: Data, domain: String) {
+ let publicKeyCredentialProvider = ASAuthorizationPlatformPublicKeyCredentialProvider(
+ relyingPartyIdentifier: domain
+ )
+
+ let assertionRequest = publicKeyCredentialProvider.createCredentialAssertionRequest(
+ challenge: challenge
+ )
+
+ let authController = ASAuthorizationController(authorizationRequests: [assertionRequest])
+ authController.delegate = self
+ authController.presentationContextProvider = self
+ authController.performRequests()
+}
+```
+
+Submit the assertion:
+
+```swift
+func submitLogin(credential: ASAuthorizationPlatformPublicKeyCredentialAssertion) async throws {
+ let credentialDict: [String: Any] = [
+ "id": credential.credentialID.base64URLEncodedString(),
+ "rawId": credential.credentialID.base64URLEncodedString(),
+ "type": "public-key",
+ "response": [
+ "clientDataJSON": credential.rawClientDataJSON.base64URLEncodedString(),
+ "authenticatorData": credential.rawAuthenticatorData.base64URLEncodedString(),
+ "signature": credential.signature.base64URLEncodedString(),
+ "userHandle": credential.userID.base64URLEncodedString()
+ ]
+ ]
+
+ let credentialJSON = try JSONSerialization.data(withJSONObject: credentialDict)
+ let credentialString = String(data: credentialJSON, encoding: .utf8)!
+
+ var request = URLRequest(url: URL(string: "\(oryBaseURL)/self-service/login?flow=\(flowId)")!)
+ request.httpMethod = "POST"
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
+
+ let payload: [String: Any] = [
+ "method": "passkey",
+ "passkey_login": credentialString
+ ]
+
+ request.httpBody = try JSONSerialization.data(withJSONObject: payload)
+ let (data, response) = try await URLSession.shared.data(for: request)
+ // Handle response...
+}
+```
+
+#### Base64URL encoding
+
+iOS requires Base64URL encoding (not standard Base64) for WebAuthn:
+
+```swift
+extension Data {
+ func base64URLEncodedString() -> String {
+ return self.base64EncodedString()
+ .replacingOccurrences(of: "+", with: "-")
+ .replacingOccurrences(of: "/", with: "_")
+ .replacingOccurrences(of: "=", with: "")
+ }
+
+ init?(base64URLEncoded string: String) {
+ var base64 = string
+ .replacingOccurrences(of: "-", with: "+")
+ .replacingOccurrences(of: "_", with: "/")
+ let remainder = base64.count % 4
+ if remainder > 0 {
+ base64 += String(repeating: "=", count: 4 - remainder)
+ }
+ self.init(base64Encoded: base64)
+ }
+}
+```
+
+### Android implementation
+
+Android passkey support uses the [Credential Manager API](https://developer.android.com/training/sign-in/passkeys). The
+integration pattern is similar to iOS.
+
+#### Dependencies
+
+Add the Credential Manager dependency to your `build.gradle`:
+
+```gradle
+dependencies {
+ implementation "androidx.credentials:credentials:1.2.0"
+ implementation "androidx.credentials:credentials-play-services-auth:1.2.0"
+}
+```
+
+#### Registration flow
+
+Initialize the registration flow:
+
+```kotlin
+suspend fun initializeRegistrationFlow(): FlowResponse {
+ val url = URL("$oryBaseURL/self-service/registration/api")
+ val connection = url.openConnection() as HttpURLConnection
+ connection.requestMethod = "GET"
+ connection.setRequestProperty("Accept", "application/json")
+
+ val response = connection.inputStream.bufferedReader().readText()
+ return parseFlowResponse(response)
+}
+```
+
+Parse the WebAuthn challenge:
+
+```kotlin
+fun extractRegistrationChallenge(flow: FlowResponse): String? {
+ val ui = flow.raw["ui"] as? Map<*, *> ?: return null
+ val nodes = ui["nodes"] as? List<*> ?: return null
+
+ for (node in nodes) {
+ val nodeMap = node as? Map<*, *> ?: continue
+ val attributes = nodeMap["attributes"] as? Map<*, *> ?: continue
+ val name = attributes["name"] as? String ?: continue
+
+ if (name == "passkey_create_data") {
+ val valueStr = attributes["value"] as? String ?: continue
+ val json = JSONObject(valueStr)
+ val credentialOptions = json.getJSONObject("credentialOptions")
+ val publicKey = credentialOptions.getJSONObject("publicKey")
+ return publicKey.getString("challenge")
+ }
+ }
+ return null
+}
+```
+
+Create the passkey:
+
+```kotlin
+suspend fun signUpWith(userName: String, requestJson: String, context: Context) {
+ val credentialManager = CredentialManager.create(context)
+
+ val request = CreatePublicKeyCredentialRequest(requestJson)
+
+ try {
+ val result = credentialManager.createCredential(
+ request = request,
+ context = context as Activity
+ ) as CreatePublicKeyCredentialResponse
+
+ submitRegistration(result.registrationResponseJson)
+ } catch (e: CreateCredentialException) {
+ // Handle error
+ }
+}
+```
+
+Submit the credential:
+
+```kotlin
+suspend fun submitRegistration(credentialJson: String) {
+ val url = URL("$oryBaseURL/self-service/registration?flow=$flowId")
+ val connection = url.openConnection() as HttpURLConnection
+ connection.requestMethod = "POST"
+ connection.setRequestProperty("Content-Type", "application/json")
+ connection.doOutput = true
+
+ val payload = JSONObject().apply {
+ put("method", "passkey")
+ put("passkey_register", credentialJson)
+ put("traits", JSONObject().apply {
+ put("email", userName)
+ })
+ }
+
+ connection.outputStream.write(payload.toString().toByteArray())
+ val response = connection.inputStream.bufferedReader().readText()
+ // Handle response...
+}
+```
+
+#### Login flow
+
+Initialize the login flow:
+
+```kotlin
+suspend fun initializeLoginFlow(): FlowResponse {
+ val url = URL("$oryBaseURL/self-service/login/api")
+ val connection = url.openConnection() as HttpURLConnection
+ connection.requestMethod = "GET"
+ connection.setRequestProperty("Accept", "application/json")
+
+ val response = connection.inputStream.bufferedReader().readText()
+ return parseFlowResponse(response)
+}
+```
+
+Parse the WebAuthn challenge:
+
+```kotlin
+fun extractLoginChallenge(flow: FlowResponse): String? {
+ val ui = flow.raw["ui"] as? Map<*, *> ?: return null
+ val nodes = ui["nodes"] as? List<*> ?: return null
+
+ for (node in nodes) {
+ val nodeMap = node as? Map<*, *> ?: continue
+ val attributes = nodeMap["attributes"] as? Map<*, *> ?: continue
+ val name = attributes["name"] as? String ?: continue
+
+ if (name == "passkey_challenge") {
+ val valueStr = attributes["value"] as? String ?: continue
+ val json = JSONObject(valueStr)
+ val publicKey = json.getJSONObject("publicKey")
+ return publicKey.getString("challenge")
+ }
+ }
+ return null
+}
+```
+
+Authenticate with passkey:
+
+```kotlin
+suspend fun signInWith(requestJson: String, context: Context) {
+ val credentialManager = CredentialManager.create(context)
+
+ val request = GetPublicKeyCredentialOption(requestJson)
+ val getCredRequest = GetCredentialRequest(listOf(request))
+
+ try {
+ val result = credentialManager.getCredential(
+ request = getCredRequest,
+ context = context as Activity
+ )
+
+ val credential = result.credential as PublicKeyCredential
+ submitLogin(credential.authenticationResponseJson)
+ } catch (e: GetCredentialException) {
+ // Handle error
+ }
+}
+```
+
+Submit the assertion:
+
+```kotlin
+suspend fun submitLogin(credentialJson: String) {
+ val url = URL("$oryBaseURL/self-service/login?flow=$flowId")
+ val connection = url.openConnection() as HttpURLConnection
+ connection.requestMethod = "POST"
+ connection.setRequestProperty("Content-Type", "application/json")
+ connection.doOutput = true
+
+ val payload = JSONObject().apply {
+ put("method", "passkey")
+ put("passkey_login", credentialJson)
+ }
+
+ connection.outputStream.write(payload.toString().toByteArray())
+ val response = connection.inputStream.bufferedReader().readText()
+ // Handle response...
+}
+```
+
+## API response handling
+
+### Flow response structure
+
+All registration and login flows return a similar structure:
+
+```json
+{
+ "id": "flow-id-uuid",
+ "type": "api",
+ "expires_at": "2025-11-23T12:00:00Z",
+ "issued_at": "2025-11-23T11:00:00Z",
+ "request_url": "https://example.com/self-service/registration/api",
+ "ui": {
+ "action": "https://example.com/self-service/registration?flow=flow-id-uuid",
+ "method": "POST",
+ "nodes": [...]
+ }
+}
+```
+
+### Node types to parse
+
+Registration flows contain these key nodes:
+
+- `csrf_token` (group: default): CSRF protection token
+- `traits.email` (group: default): User identity traits
+- `passkey_create_data` (group: passkey): WebAuthn creation options
+- `passkey_register` (group: passkey): Where to submit the credential
+
+Login flows contain:
+
+- `csrf_token` (group: default): CSRF protection token
+- `identifier` (group: default): Optional username field
+- `passkey_challenge` (group: passkey): WebAuthn assertion options
+- `passkey_login` (group: passkey): Where to submit the assertion
+
+### Success response
+
+On successful authentication, Ory returns a session:
+
+```json
+{
+ "session": {
+ "id": "session-id-uuid",
+ "active": true,
+ "expires_at": "2025-11-23T13:00:00Z",
+ "authenticated_at": "2025-11-23T11:00:00Z",
+ "authenticator_assurance_level": "aal1",
+ "authentication_methods": [
+ {
+ "method": "passkey",
+ "aal": "aal1",
+ "completed_at": "2025-11-23T11:00:00Z"
+ }
+ ],
+ "identity": {
+ "id": "identity-id-uuid",
+ "schema_id": "default",
+ "traits": {
+ "email": "user@example.com"
+ }
+ }
+ }
+}
+```
+
+Store the session token for authenticated requests. On mobile, use secure storage (iOS Keychain, Android Keystore).
+
+## Error handling
+
+### Common errors
+
+#### Invalid WebAuthn response
+
+```json
+{
+ "error": {
+ "id": "browser_location_change_required",
+ "code": 422,
+ "status": "Unprocessable Entity",
+ "reason": "Unable to parse WebAuthn response: Parse error for Registration"
+ }
+}
+```
+
+This error occurs when there is incorrect Base64URL encoding (using standard Base64 instead), missing required fields in the
+credential response, or malformed JSON in `passkey_register` or `passkey_login` fields.
+
+To resolve this, verify your Base64URL encoding and ensure all required WebAuthn response fields are included.
+
+#### Domain mismatch
+
+```json
+{
+ "error": {
+ "id": "security_identity_mismatch",
+ "code": 400,
+ "status": "Bad Request",
+ "reason": "The request was malformed or contained invalid parameters"
+ }
+}
+```
+
+This error occurs when the Associated Domains or `assetlinks.json` domain doesn't match Kratos `rp.id`, the AASA or
+`assetlinks.json` file isn't properly served, or the application uses HTTP instead of HTTPS.
+
+To resolve this, verify your Associated Domains entitlement matches your Kratos configuration. Test AASA file accessibility using
+`curl https://ory.your-custom-domain.com/.well-known/apple-app-site-association`. Test `assetlinks.json` using
+`curl https://ory.your-custom-domain.com/.well-known/assetlinks.json`. Ensure HTTPS with valid certificate.
+
+#### Flow expired
+
+```json
+{
+ "error": {
+ "id": "self_service_flow_expired",
+ "code": 410,
+ "status": "Gone",
+ "reason": "The self-service flow has expired"
+ }
+}
+```
+
+This error occurs when the user took too long to complete the flow (default: 1 hour).
+
+To resolve this, initialize a new flow and retry the operation.
+
+#### User canceled
+
+On mobile platforms, users can cancel the passkey prompt. Handle this gracefully.
+
+```mdx-code-block
+
+
+```
+
+```swift
+func authorizationController(controller: ASAuthorizationController,
+ didCompleteWithError error: Error) {
+ if let authError = error as? ASAuthorizationError,
+ authError.code == .canceled {
+ // User canceled - show alternative login options
+ }
+}
+```
+
+```mdx-code-block
+
+
+```
+
+```kotlin
+catch (e: GetCredentialException) {
+ when (e) {
+ is GetCredentialCancellationException -> {
+ // User canceled - show alternative login options
+ }
+ }
+}
+```
+
+```mdx-code-block
+
+
+```
+
+### Mobile-specific issues
+
+#### AASA file not found on iOS
+
+The passkey prompt doesn't appear or the user sees "No credentials available" errors.
+
+To troubleshoot this issue:
+
+1. Verify the AASA file is accessible via browser.
+2. Check the file has correct `Content-Type: application/json`.
+3. Verify the Team ID and Bundle ID are correct.
+4. Try uninstalling and reinstalling the app.
+5. Check the device isn't using a VPN that blocks the domain.
+
+#### assetlinks.json validation failed on Android
+
+The user sees "No credentials found" errors or the passkey dialog doesn't show.
+
+To troubleshoot this issue:
+
+1. Verify the `assetlinks.json` file is accessible via browser.
+2. Use the [Statement List Generator and Tester](https://developers.google.com/digital-asset-links/tools/generator) to validate.
+3. Verify the SHA-256 fingerprint matches your signing key.
+4. Ensure the package name matches exactly.
+5. Clear app data and retry.
+
+#### Domain not HTTPS
+
+Both iOS and Android require HTTPS for passkeys. HTTP domains fail silently or with cryptic errors.
+
+To resolve this, use HTTPS with a valid TLS certificate. For local development, use a tool like [ngrok](https://ngrok.com/) to
+create an HTTPS tunnel.
+
+## Best practices
+
+### Session management
+
+Store session tokens securely using iOS Keychain or Android Keystore. Handle session expiration by checking session validity
+before making authenticated requests. Implement token refresh using Ory's `/sessions/whoami` endpoint to verify sessions.
+
+### User experience
+
+Provide fallback options to allow users to sign in with other methods if passkeys fail. Handle errors gracefully by showing
+user-friendly error messages. Support AutoFill on iOS 17+ and Android 14+ to enable AutoFill-assisted passkey sign-in for better
+user experience.
+
+### Testing
+
+Test on physical devices as passkeys don't work reliably in simulators or emulators. Test with multiple accounts to verify
+credential isolation. Test cross-platform to ensure passkeys created on one platform work on others via cloud sync.
+
+## Next steps
+
+Review the [Passkeys overview](./05_passkeys.mdx) for configuration options. See [Self-Service Flows](../self-service.mdx) for
+more flow details. Check out the [API documentation](../reference/api.mdx) for complete endpoint reference.
diff --git a/docs/kratos/passwordless/06_code.mdx b/docs/kratos/passwordless/07_code.mdx
similarity index 100%
rename from docs/kratos/passwordless/06_code.mdx
rename to docs/kratos/passwordless/07_code.mdx
diff --git a/package-lock.json b/package-lock.json
index e9da96630..a0c7b8856 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -270,6 +270,7 @@
"version": "5.44.0",
"resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.44.0.tgz",
"integrity": "sha512-/FRKUM1G4xn3vV8+9xH1WJ9XknU8rkBGlefruq9jDhYUAvYozKimhrmC2pRqw/RyHhPivmgZCRuC8jHP8piz4Q==",
+ "peer": true,
"dependencies": {
"@algolia/client-common": "5.44.0",
"@algolia/requester-browser-xhr": "5.44.0",
@@ -446,6 +447,7 @@
"node_modules/@babel/core": {
"version": "7.26.0",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.26.0",
@@ -2432,6 +2434,7 @@
"url": "https://opencollective.com/csstools"
}
],
+ "peer": true,
"engines": {
"node": ">=18"
},
@@ -2453,6 +2456,7 @@
"url": "https://opencollective.com/csstools"
}
],
+ "peer": true,
"engines": {
"node": ">=18"
}
@@ -2557,6 +2561,7 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
"integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
+ "peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -2962,6 +2967,7 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
"integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
+ "peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -3900,6 +3906,7 @@
"version": "3.9.2",
"resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.9.2.tgz",
"integrity": "sha512-C5wZsGuKTY8jEYsqdxhhFOe1ZDjH0uIYJ9T/jebHwkyxqnr4wW0jTkB72OMqNjsoQRcb0JN3PcSeTwFlVgzCZg==",
+ "peer": true,
"dependencies": {
"@docusaurus/core": "3.9.2",
"@docusaurus/logger": "3.9.2",
@@ -4184,6 +4191,7 @@
"version": "3.9.2",
"resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.9.2.tgz",
"integrity": "sha512-6c4DAbR6n6nPbnZhY2V3tzpnKnGL+6aOsLvFL26VRqhlczli9eWG0VDUNoCQEPnGwDMhPS42UhSAnz5pThm5Ag==",
+ "peer": true,
"dependencies": {
"@docusaurus/mdx-loader": "3.9.2",
"@docusaurus/module-type-aliases": "3.9.2",
@@ -4287,6 +4295,7 @@
"version": "3.9.2",
"resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.9.2.tgz",
"integrity": "sha512-lBSBiRruFurFKXr5Hbsl2thmGweAPmddhF3jb99U4EMDA5L+e5Y1rAkOS07Nvrup7HUMBDrCV45meaxZnt28nQ==",
+ "peer": true,
"dependencies": {
"@docusaurus/logger": "3.9.2",
"@docusaurus/types": "3.9.2",
@@ -4344,6 +4353,17 @@
"node": ">=20.0"
}
},
+ "node_modules/@emnapi/runtime": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz",
+ "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@emotion/is-prop-valid": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz",
@@ -4457,6 +4477,33 @@
"@img/sharp-libvips-darwin-arm64": "1.0.2"
}
},
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.33.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.3.tgz",
+ "integrity": "sha512-2QeSl7QDK9ru//YBT4sQkoq7L0EAJZA3rtV+v9p8xTKl4U1bUqTIaCnoC7Ctx2kCjQgwFXDasOtPTCT8eCTXvw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "glibc": ">=2.26",
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.0.2"
+ }
+ },
"node_modules/@img/sharp-libvips-darwin-arm64": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz",
@@ -4479,6 +4526,398 @@
"url": "https://opencollective.com/libvips"
}
},
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz",
+ "integrity": "sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "macos": ">=10.13",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz",
+ "integrity": "sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "glibc": ">=2.28",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz",
+ "integrity": "sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "glibc": ">=2.26",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz",
+ "integrity": "sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "glibc": ">=2.28",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz",
+ "integrity": "sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "glibc": ">=2.26",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz",
+ "integrity": "sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "musl": ">=1.2.2",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz",
+ "integrity": "sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "musl": ">=1.2.2",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.33.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.3.tgz",
+ "integrity": "sha512-Q7Ee3fFSC9P7vUSqVEF0zccJsZ8GiiCJYGWDdhEjdlOeS9/jdkyJ6sUSPj+bL8VuOYFSbofrW0t/86ceVhx32w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "glibc": ">=2.28",
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.0.2"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.33.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.3.tgz",
+ "integrity": "sha512-Zf+sF1jHZJKA6Gor9hoYG2ljr4wo9cY4twaxgFDvlG0Xz9V7sinsPp8pFd1XtlhTzYo0IhDbl3rK7P6MzHpnYA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "glibc": ">=2.26",
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.0.2"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.33.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.3.tgz",
+ "integrity": "sha512-vFk441DKRFepjhTEH20oBlFrHcLjPfI8B0pMIxGm3+yilKyYeHEVvrZhYFdqIseSclIqbQ3SnZMwEMWonY5XFA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "glibc": ">=2.28",
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.0.2"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.33.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.3.tgz",
+ "integrity": "sha512-Q4I++herIJxJi+qmbySd072oDPRkCg/SClLEIDh5IL9h1zjhqjv82H0Seupd+q2m0yOfD+/fJnjSoDFtKiHu2g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "glibc": ">=2.26",
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.0.2"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.33.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.3.tgz",
+ "integrity": "sha512-qnDccehRDXadhM9PM5hLvcPRYqyFCBN31kq+ErBSZtZlsAc1U4Z85xf/RXv1qolkdu+ibw64fUDaRdktxTNP9A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "musl": ">=1.2.2",
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.2"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.33.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.3.tgz",
+ "integrity": "sha512-Jhchim8kHWIU/GZ+9poHMWRcefeaxFIs9EBqf9KtcC14Ojk6qua7ghKiPs0sbeLbLj/2IGBtDcxHyjCdYWkk2w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "musl": ">=1.2.2",
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.2"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.33.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.3.tgz",
+ "integrity": "sha512-68zivsdJ0koE96stdUfM+gmyaK/NcoSZK5dV5CAjES0FUXS9lchYt8LAB5rTbM7nlWtxaU/2GON0HVN6/ZYJAQ==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.1.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.33.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.3.tgz",
+ "integrity": "sha512-CyimAduT2whQD8ER4Ux7exKrtfoaUiVr7HG0zZvO0XTFn2idUWljjxv58GxNTkFb8/J9Ub9AqITGkJD6ZginxQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.33.3",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.3.tgz",
+ "integrity": "sha512-viT4fUIDKnli3IfOephGnolMzhz5VaTvDRkYqtZxOMIoMQ4MrAziO7pT1nVnOt2FAm7qW5aa+CCc13aEY6Le0g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0",
+ "npm": ">=9.6.5",
+ "pnpm": ">=7.1.0",
+ "yarn": ">=3.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
"node_modules/@isaacs/fs-minipass": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
@@ -5039,6 +5478,7 @@
"node_modules/@mdx-js/react": {
"version": "3.1.0",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@types/mdx": "^2.0.0"
},
@@ -5098,6 +5538,7 @@
"node_modules/@octokit/core": {
"version": "5.2.0",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@octokit/auth-token": "^4.0.0",
"@octokit/graphql": "^7.1.0",
@@ -5446,6 +5887,7 @@
"node_modules/@rjsf/core": {
"version": "5.24.1",
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
@@ -5466,6 +5908,7 @@
"resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.24.1.tgz",
"integrity": "sha512-A25fFj/TNz5bKikCIs20DiedKAalLuAQ7vUX9VQkD2hps5C9YVr0dJgSlsPa5kzl6lQMaRsNouTx8E1ZdLV2fg==",
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"json-schema-merge-allof": "^0.8.1",
"jsonpointer": "^5.0.1",
@@ -5755,6 +6198,7 @@
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz",
"integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
+ "peer": true,
"dependencies": {
"@babel/core": "^7.21.3",
"@svgr/babel-preset": "8.1.0",
@@ -5854,6 +6298,7 @@
"dev": true,
"hasInstallScript": true,
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"@swc/counter": "^0.1.2",
"@swc/types": "^0.1.5"
@@ -5916,6 +6361,142 @@
"node": ">=10"
}
},
+ "node_modules/@swc/core-linux-arm-gnueabihf": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.8.tgz",
+ "integrity": "sha512-PP9JIJt19bUWhAGcQW6qMwTjZOcMyzkvZa0/LWSlDm0ORYVLmDXUoeQbGD3e0Zju9UiZxyulnpjEN0ZihJgPTA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm64-gnu": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.8.tgz",
+ "integrity": "sha512-HvEWnwKHkoVUr5iftWirTApFJ13hGzhAY2CMw4lz9lur2m+zhPviRRED0FCI6T95Knpv7+8eUOr98Z7ctrG6DQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-arm64-musl": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.8.tgz",
+ "integrity": "sha512-kY8+qa7k/dEeBq9p0Hrta18QnJPpsiJvDQSLNaTIFpdM3aEM9zbkshWz8gaX5VVGUEALowCBUWqmzO4VaqM+2w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-x64-gnu": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.8.tgz",
+ "integrity": "sha512-0WWyIw432wpO/zeGblwq4f2YWam4pn8Z/Ig4KzHMgthR/KmiLU3f0Z7eo45eVmq5vcU7Os1zi/Zb65OOt09q/w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-linux-x64-musl": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.8.tgz",
+ "integrity": "sha512-p4yxvVS05rBNCrBaSTa20KK88vOwtg8ifTW7ec/yoab0bD5EwzzB8KbDmLLxE6uziFa0sdjF0dfRDwSZPex37Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-arm64-msvc": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.8.tgz",
+ "integrity": "sha512-jKuXihxAaqUnbFfvPxtmxjdJfs87F1GdBf33il+VUmSyWCP4BE6vW+/ReDAe8sRNsKyrZ3UH1vI5q1n64csBUA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-ia32-msvc": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.8.tgz",
+ "integrity": "sha512-O0wT4AGHrX8aBeH6c2ADMHgagAJc5Kf6W48U5moyYDAkkVnKvtSc4kGhjWhe1Yl0sI0cpYh2In2FxvYsb44eWw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swc/core-win32-x64-msvc": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.8.tgz",
+ "integrity": "sha512-C2AYc3A2o+ECciqsJWRgIpp83Vk5EaRzHe7ed/xOWzVd0MsWR+fweEsyOjlmzHfpUxJSi46Ak3/BIZJlhZbXbg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/@swc/counter": {
"version": "0.1.3",
"dev": true,
@@ -6694,7 +7275,8 @@
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/@types/keyv": {
"version": "3.1.4",
@@ -6726,6 +7308,7 @@
"node_modules/@types/node": {
"version": "22.8.4",
"license": "MIT",
+ "peer": true,
"dependencies": {
"undici-types": "~6.19.8"
}
@@ -6768,6 +7351,7 @@
"node_modules/@types/react": {
"version": "18.3.12",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
@@ -7090,6 +7674,7 @@
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -7170,6 +7755,7 @@
"node_modules/ajv": {
"version": "8.17.1",
"license": "MIT",
+ "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -7211,6 +7797,7 @@
"version": "5.44.0",
"resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.44.0.tgz",
"integrity": "sha512-f8IpsbdQjzTjr/4mJ/jv5UplrtyMnnciGax6/B0OnLCs2/GJTK13O4Y7Ff1AvJVAaztanH+m5nzPoUq6EAy+aA==",
+ "peer": true,
"dependencies": {
"@algolia/abtesting": "1.10.0",
"@algolia/client-abtesting": "5.44.0",
@@ -7503,6 +8090,7 @@
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
+ "peer": true,
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",
@@ -7999,6 +8587,7 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.8.25",
"caniuse-lite": "^1.0.30001754",
@@ -8360,6 +8949,7 @@
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz",
"integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==",
+ "peer": true,
"dependencies": {
"@chevrotain/cst-dts-gen": "11.0.3",
"@chevrotain/gast": "11.0.3",
@@ -9001,6 +9591,7 @@
"version": "3.38.1",
"hasInstallScript": true,
"license": "MIT",
+ "peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
@@ -9290,6 +9881,7 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
"integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
+ "peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -9600,6 +10192,7 @@
"node_modules/cytoscape": {
"version": "3.31.0",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=0.10"
}
@@ -9965,6 +10558,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -11501,6 +12095,7 @@
"node_modules/file-loader/node_modules/ajv": {
"version": "6.12.6",
"license": "MIT",
+ "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -14162,6 +14757,7 @@
"node_modules/jsep": {
"version": "1.4.0",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">= 10.16.0"
}
@@ -17321,6 +17917,7 @@
"resolved": "https://registry.npmjs.org/mobx/-/mobx-6.13.7.tgz",
"integrity": "sha512-aChaVU/DO5aRPmk1GX8L+whocagUUpBQqoPtJk+cm7UOXUk87J4PeWCh6nNmTTIfEhiR9DI/+FnA8dln/hTK7g==",
"license": "MIT",
+ "peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mobx"
@@ -17800,6 +18397,7 @@
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -18780,6 +19378,7 @@
}
],
"license": "MIT",
+ "peer": true,
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.1.1",
@@ -19635,6 +20234,7 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
"integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
+ "peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -20203,6 +20803,7 @@
"version": "3.2.5",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@@ -20592,6 +21193,7 @@
"version": "6.12.6",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -20656,6 +21258,7 @@
"node_modules/react": {
"version": "19.0.0",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -20665,6 +21268,7 @@
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.6.8.tgz",
"integrity": "sha512-yD6uN78XlFOkETQp6GRuVe0s5509x3XYx8PfPbirwFTYCj5/RfmSs9YZGCwkUrhZNFzj7tZPdpb+3k50mK1E4g==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/runtime": "^7.14.0",
"@restart/context": "^2.1.4",
@@ -20692,6 +21296,7 @@
"node_modules/react-dom": {
"version": "19.0.0",
"license": "MIT",
+ "peer": true,
"dependencies": {
"scheduler": "^0.25.0"
},
@@ -20744,6 +21349,7 @@
"name": "@docusaurus/react-loadable",
"version": "6.0.0",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@types/react": "*"
},
@@ -20788,6 +21394,7 @@
"node_modules/react-router": {
"version": "5.3.4",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/runtime": "^7.12.13",
"history": "^4.9.0",
@@ -23238,6 +23845,7 @@
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.18.tgz",
"integrity": "sha512-Mvf3gJFzZCkhjY2Y/Fx9z1m3dxbza0uI9H1CbNZm/jSHCojzJhQ0R7bByrlFJINnMzz/gPulpoFFGymNwrsMcw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@emotion/is-prop-valid": "1.2.2",
"@emotion/unitless": "0.8.1",
@@ -23522,6 +24130,7 @@
"node_modules/terser-webpack-plugin/node_modules/ajv": {
"version": "6.12.6",
"license": "MIT",
+ "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -23950,6 +24559,7 @@
"version": "10.9.1",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
@@ -23995,7 +24605,8 @@
},
"node_modules/tslib": {
"version": "2.8.0",
- "license": "0BSD"
+ "license": "0BSD",
+ "peer": true
},
"node_modules/tty-browserify": {
"version": "0.0.1",
@@ -24476,6 +25087,7 @@
"node_modules/url-loader/node_modules/ajv": {
"version": "6.12.6",
"license": "MIT",
+ "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -24808,6 +25420,7 @@
"node_modules/webpack": {
"version": "5.95.0",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@types/estree": "^1.0.5",
"@webassemblyjs/ast": "^1.12.1",
@@ -25088,6 +25701,7 @@
"node_modules/webpack/node_modules/ajv": {
"version": "6.12.6",
"license": "MIT",
+ "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -25611,6 +26225,7 @@
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz",
"integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
+ "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
diff --git a/src/sidebar.ts b/src/sidebar.ts
index 8dd49c751..35d0a3509 100644
--- a/src/sidebar.ts
+++ b/src/sidebar.ts
@@ -358,6 +358,7 @@ const kratos: SidebarItemsConfig = [
"kratos/passwordless/passwordless",
"kratos/passwordless/one-time-code",
"kratos/passwordless/passkeys",
+ "kratos/passwordless/passkeys-mobile",
"kratos/organizations/organizations",
"kratos/emails-sms/custom-email-templates",
],