# Passkeys Authentication

div
div
Client SDK
div
Mobile approve
div
SSO
div
Sub-journey
> Authenticates the user with Webauthn/passkeys


## Description

This step is used to complete a flow that authenticates the user using WebAuthn biometrics. The biometric credentials must be registered using the [Register Passkeys](/guides/orchestration/journeys/register_webauthn) step.

The authentication process starts with a [Login Form](/guides/orchestration/journeys/login_form) step that is configured to enable passkeys. Once triggered, this step instructs the client to authenticate passkey credentials on the device. The client does this using the WebAuthn SDK calls, which will prompt the user for biometrics (e.g., fingerprint). If successful, the SDK returns an encoded result. The client submits this result to the journey using the Orchestration SDK call, and the result is stored in the form output variable.

The journey then proceeds to the passkey branch, which executes the **Passkey Authentication** step to complete the flow. This step completes the Passkey authentication based on the encoded result obtained from the form (without involving any user interaction). If successful, the journey sets the user context to the authenticated user and continues to the next step. User tokens generated upon authentication can be accessed in subsequent steps using `@policy.userContext()`. If it fails, the journey proceeds to a failure branch (if configured); otherwise, the journey is aborted and an error is sent to the client.

To support passkey authentication, you'll need to:

- Implement passkey registration, as described [here](/guides/orchestration/journeys/register_webauthn)
- Configure the [Login Form](/guides/orchestration/journeys/login_form) step, and build the supporting UI
- Implement the WebAuthn SDK calls required for authentication
- Implement the Orchestration SDK call that submits input for the login form step
- Configure the **Passkeys authentication** journey step


Requirements for passkey authentication include:

- **Web**: modern OS (macOS 13+, Windows 10+) and browser (e.g., Google Chrome 109+),
- **Android**: Android 9+, device with registered biometrics (e.g., Face or Fingerprint), device with support for Google Play Services, `compileSdk` 34 and onward, `minSdk` 23
- **iOS**: iOS 16.0+, Xcode 14.0+, device with registered biometrics (e.g., FaceID or TouchID), registered AppleID, enabled iCloud KeyChain


For more, see quickstart guides: [Web](/guides/webauthn/quick_start_sdk), [Android](/guides/webauthn/quick_start_sdk_android), and [iOS](/guides/webauthn/quick_start_sdk_ios).

## Verifying bound devices

After a user signs in with a passkey, you can determine whether the current device is recognized and decide how the journey should proceed (for example, allow, restrict, or require step-up authentication).

Device recognition depends on **device binding**, which associates a specific device with a user for a given application. This binding is established during passkey registration when device information is included (see [Device keys and passkey registration](/guides/webauthn/device_passkeys_binding)).

To build trust-based logic in your flow, use the **[Known Device Status](/guides/orchestration/journeys/validate_device)** step within the journey. This step verifies whether the current device is a registered (bound) device using a cryptographic challenge/response mechanism, and allows you to adapt the journey accordingly.

For additional approaches (such as using the `device_keys` claim in the ID token), see [Device keys and passkey registration](/guides/webauthn/device_passkeys_binding).

## Configuration

div
| Field | Description |
|  --- | --- |
| **Encoded Result** | WebAuthn encoded result returned by the client to the login form upon successful passkey authentication. |
| **Org context** | **Only in B2B journeys**. Determines the organization for which the step is executed. By default, the step uses the org context previously set in the journey (e.g., using [Select organization](/guides/orchestration/journeys/select_organization) step). If set to "manual", you can provide an expression that yields the organization ID. |
| **Error Output Variable** | Name of the variable that contains error data returned by the client |
| **Failure Behavior** | Determines the behavior in case of failure, which either aborts the journey (default) or proceeds to a failure branch of the control flow. |


## Example

Consider a login form that authenticates the user using passkeys. In our example, the form ID is `loginForm`, the input will be stored in a variable named `loginData`, and only **Passkeys** is enabled. The passkey branch has the default branch ID (`passkeys`) and default schema (which expects `webauthn_encoded_result` from the client).

The passkey branch contains the **Passkey Authentication** step, which obtains the encoded result from the login form using `loginData.webauthn_encoded_result`:

![](/assets/auth_passkeys_ex1.10780158e39f5b7a8eaa13140c4ff049982978aa48bdec63caf9115c3d6d2f2a.6f9096f8.png)

When executed, the **Login Form** step returns the `idoServiceResponse` object to the client. Based on the form ID passed in `journeyStepId`, the client presents a form that initiates a WebAuthn authentication process using WebAuthn SDK calls. This will prompt the user to authenticate using biometrics. If successful, the SDK returns the WebAuthn encoded result and the client submits it to the journey to complete the login form step. For example:

JavaScript

```Js
// Authenticates passkey credentials using a modal experience
// If SDK was loaded via script tag, invoke functions inside 'window.tsPlatform'
let authnResult = await webauthn.authenticate.modal();

// Submits to the journey the encoded result returned by the WebAuthn SDK call
// If SDK was loaded via script tag, invoke functions inside 'window.tsPlatform'
let nextIdoServiceResponse = await ido.submitClientResponse(
    "passkeys",
    {
        "webauthn_encoded_result": authnResult
    })
```

Kotlin

```kotlin
data class WebAuthnAuthenticationResult(val webauthn_encoded_result: String)
/** Expected data structure:
 {
   "webauthn_encoded_result": "[WEBAUTHN_AUTHENTICATION_RESULT]",
 }*/

// ...
fun authenticatePasskeys(userName: String) {
    TSAuthentication.authenticateWebAuthn(activityBasedContext, userName, object: TSAuthCallback<AuthenticationResult, TSWebAuthnAuthenticationError> {
        override fun error(error: TSWebAuthnAuthenticationError) {
            // Handle error
        }

        override fun success(authResult: AuthenticationResult) {
            // Submits to the journey the encoded result returned by the WebAuthn SDK call
            TSIdo.submitClientResponse("passkeys", WebAuthnAuthenticationResult(authResult.result()), callback)
        }
    })
}
```

Swift

```swift
ido {
    TSAuthentication.shared.authenticateWebAuthn(username: "[USER_ID]") { response in
        switch response {
        case .success(let result):
            let data: [String: Any] = ["webauthn_encoded_result": result.result]
            try TSIdo.submitClientResponse(clientResponseOptionId: .custom(id: "passkeys"), data: data)
        case .failure(let error):
            // Handle error
        }
    }

} catch {
    // Handle error
}
```

The journey stores the encoded result in the output variable configured for the login form, and proceeds to the **Passkey Authentication** step. When executed, this step uses the encoded result (`loginData.webauthn_encoded_result`) to complete the authentication.