Passkey Authentication

Early Access

Introduction

Passkey Registration

This document assumes that a User has been added to an Identity Store and that the User has registered a passkey. Please follow the Passkey Registration guide first before continuing to the authentication process.

Authenticating via Process Definition

Step 1 - Create a Process Definition

  1. Create a new Process Definition by navigating to Process Definitions > New Process Definition in the Backoffice application.

  2. Add a 'Create Start Event' to the Process Designer.


  1. Click the 'Start' event to open the right-side contextual menu and enter a name for the 'Name' input parameter.

  2. Add a 'Create End Event' to the Process Designer and connect it to the 'Start' event using a sequence flow arrow.


  1. Click the 'End' event to open the right-side contextual menu and enter a name for the 'Name' input parameter.

Step 2 - Start the Authentication Process

The 'Start Passkey Authentication' activity is used to begin the authentication process.

  1. In the Process Designer, add the 'Start Passkey Authentication' activity after the 'start' event and connect using a sequence flow arrow.


  1. The 'Start Passkey Authentication' provides 8 configurable parameters described below:

Input Parameter

Type

Description

Authentication Policy

String

The authentication policy to use for the authentication process. See auto$ for information on configuring an authentication policy.

Challenge

String

The signed challenge is represented by a public key registered to the User. This value is auto-generated.

Passkey Authentication Key

String

Required - The unique ID associated with the passkey authentication activity.

Relying Party ID

String

Required - The ID of the relying party whose passkey registration configuration will be used.

Store Name

String

Required - The name of the Identity Store.

User External ID

String

This property can be used to identify the User that the passkey registration applies to.

User ID

String

The unique ID automatically generated when the User is created. In the context of the 'Start Passkey Registration' activity, this ID can be used to identify the User that the passkey registration applies to.

User Key

String

The User Key that can be used to identify the User that the passkey authentication applies to.

  1. When the authentication process is started, the end-user will see a button that allows them to begin the authentication process.


Step 3 - Configure the Authentication Form

The authentication form is used to accept input from the end-user who will be completing the passkey authentication. In this example, the end-user will be requested to provide their 'User Name' and 'Display Name' to complete the authentication process.

  1. Create a new Custom Form from the Integration Hub > Custom Forms section of the Backoffice. For more information using the Custom Form builder, see the Custom Data Forms guide.

  2. The example below demonstrates a JSON form that requests the 'User Name' and 'Display Name'.

{ "title": { "values": { "en": "AUTHENTICATION" }, "isValid": true, "type": "title" }, "components": [ { "type": "text", "isValid": true, "readOnly": false, "id": "name", "label": { "values": { "en": "Name" } }, "default": "", "transient": true, "validate": { "required": true, "minLength": "", "maxLength": "", "missingMessage": { "values": { "en": "" } }, "invalidMessage": { "values": { "en": "" } }, "invalidLengthMessage": { "values": { "en": "" } } }, "index": 0 }, { "type": "text", "isValid": true, "readOnly": false, "id": "displayName", "label": { "values": { "en": "Display Name" } }, "default": "", "transient": true, "validate": { "required": true, "minLength": "", "maxLength": "", "missingMessage": { "values": { "en": "" } }, "invalidMessage": { "values": { "en": "" } }, "invalidLengthMessage": { "values": { "en": "" } } }, "index": 1 } ], "submitButton": { "type": "submitButton", "isValid": true, "values": { "en": "Submit for Authentication" } } }
  1. Once the Custom Form has been created, add a 'Passkey Authentication Form' activity to the Process Designer and connect to the 'Start Passkey Authentication' activity using a sequence flow arrow.


  1. The 'Passkey Authentication Form' activity includes the following Input Parameters:

Input Parameter

Type

Description

Authentication timeout (in seconds)

Integer

The allotted time to complete the form before a timeout event occurs.

Data form name

String

The Custom Form that will be displayed to the end-user.

Data form version

Integer

The version of the Custom Form.

Delay (in seconds)

Integer

If 'Hide buttons' is enabled, this parameter will configure how long the page is displayed before progressing to the next step.

Form data Key

String

The unique identifier of the 'Passkey Registration Form' activity.

Hide buttons

Boolean

If enabled, the 'continue' button will be hidden from the form. This is disabled by default, meaning buttons will be shown.

Passkey Authentication Key

String

The unique ID associated with the passkey authentication attempt.

Start Passkey Authentication via API

The passkey authentication process can be started via the API by calling either:

  • /api/identity-store/stores/{storeName}/users/{userId}/passkeys/relyingParties/{rpId}/authentications/{authPolicyName}

  • /api/identity-store/{tenantId}/stores/{storeName}/users/{userExtId}/passkeys/relyingParties/{rpId}/authentications/{authPolicyName}/ext.

Permissions: TNT#{tenant}#IdentityStore:createPasskeyAuthenticationForUser, TNT#{tenant}#IdentityStore:createPasskeyAuthenticationForUserByExtId , TNT#{tenant}#IdentityStore:createPasskeyAuthentication

Example Request:

POST https://{{tenant}}.{{region}}.trustx.com/api/identity-store/stores/{storename}/users/{userID}/passkeys/relyingParties/{rpId}/authentications/{authPolicyName} Content-Type: application/json Authorization: Bearer {{token}} { }

Example Response:

{ "clientRequest": { "rp": { "name": "passkeyConfig", "id": "skydev1.oak.trustx-dev.com" }, "user": { "id": "HJaQW0vnRHK5m-h9cl10CQ" }, "challenge": "YlDxQJClvRdoqKnaOI8hBhRelrAzXBJcVdyvgNHLeuJ4vYumkX-NIxBxCkbG6d-XdN8f8xo6yVINqJbN2-2s6g", "pubKeyCredParams": [ { "type": "public-key", "alg": -65535 }, { "type": "public-key", "alg": -257 }, { "type": "public-key", "alg": -258 }, { "type": "public-key", "alg": -259 }, { "type": "public-key", "alg": -37 }, { "type": "public-key", "alg": -38 }, { "type": "public-key", "alg": -39 }, { "type": "public-key", "alg": -7 }, { "type": "public-key", "alg": -35 }, { "type": "public-key", "alg": -36 }, { "type": "public-key", "alg": -8 }, { "type": "public-key", "alg": -43 } ], "timeout": 30000, "excludeCredentials": [], "authenticatorSelectionCriteria": { "authenticatorAttachment": "platform", "residentKey": "preferred", "requireResidentKey": false, "userVerification": "preferred" }, "attestation": "none", "extensions": { "credProps": true, "credentialProtectionPolicy": "userVerificationOptional" } }, "sessionId": "T4NJA4PEEM6Q3GXSON5QQZYMG4" }

As seen above, the response will return a sessionID, which is used to complete the passkey registration in Step 6.

Step 3 - Configure a Custom Page (Optional)

It is also possible to request the data from a Custom Page instead of a Custom Form by using the Custom Page activity.

  1. Create a new Custom Page from the Integration Hub > Custom Pages section of the Backoffice. For more information on creating Custom Pages, see the Custom Pages Guide.

  2. The example HTML below requests the 'User Name' and 'Display Name' from the end-user:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Passkey Demo</title> <link rel="stylesheet" href="style.css" /> </head> <body> <form id="registration"> <h1>Authenticate with Passkey</h1> <input id="name" name="name" placeholder="Name" value="john.doe" /> <input id="displayName" name="displayName" placeholder="Display Name" value="John Doe" /> <button type="submit">Authenticate</button> </form> <script type="module" src="app.js"></script> </body> </html>
body { margin: 0; font-family: Arial, sans-serif; background-color: #f2f4f8; display: flex; justify-content: center; align-items: center; height: 100vh; } form { background-color: white; padding: 2rem; border-radius: 12px; box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1); width: 300px; display: flex; flex-direction: column; gap: 1rem; } input { padding: 0.6rem; border: 1px solid #ccc; border-radius: 6px; font-size: 1rem; } button { padding: 0.7rem; font-size: 1rem; border: none; border-radius: 6px; background-color: #007bff; color: white; cursor: pointer; transition: background-color 0.2s ease; } button:hover { background-color: #0056b3; } h1 { margin-bottom: 1rem; font-size: 1.2rem; color: #333; text-align: center; }
import { get, parseRequestOptionsFromJSON } from "https://cdn.jsdelivr.net/npm/@github/webauthn-json@2.0.2/dist/esm/webauthn-json.browser-ponyfill.js"; const GET_ERROR = 'GET_ERROR'; const USER_CANCELED = 'USER_CANCELED'; const NOT_ALLOWED_ERROR = 'NotAllowedError'; const ABORT_ERROR = 'AbortError'; const CONFIGURATION_ERROR = 'CONFIGURATION_ERROR'; const CLIENT_TIMEOUT = 'CLIENT_TIMEOUT' const CLIENT_TIMEOUT_MESSAGE = 'User session has expired.' let passkeyAuthClientRequest; function parseError({ type, message }) { return { clientError: { errorType: type, errorMessage: message } }; }; function sendRequest(passkeyAuthClientResponse) { if (window.parent !== window) { window.parent.postMessage({ event: 'SEND', variables: { passkeyAuthClientResponse: JSON.stringify(passkeyAuthClientResponse) } }, '*'); } }; const controller = new AbortController(); window.addEventListener('message', function (event) { if (event.data.variables) { const sessionData = event.data.variables.sessionData; if (sessionData?.passkeyAuthClientRequest) { passkeyAuthClientRequest = sessionData.passkeyAuthClientRequest; } if (constants.timeoutInSeconds) { setTimeout(async () => { controller.abort() }, (constants.timeoutInSeconds - 12) * 1000) } } else { console.log('No variables received'); } }); async function authPasskey(passkeyAuthClientRequestUpdated) { let serverPublicKeyCredential; try { serverPublicKeyCredential = parseRequestOptionsFromJSON({ publicKey: { ...passkeyAuthClientRequestUpdated } }) } catch (err) { return parseError({ type: CONFIGURATION_ERROR, message: err.message }); } try { const signal = controller.signal; const response = await get({ ...serverPublicKeyCredential, signal }); return response; } catch (err) { if (err.name === NOT_ALLOWED_ERROR) { return parseError({ type: USER_CANCELED, message: err.message }); } if (err.name === ABORT_ERROR) { return parseError({ type: CLIENT_TIMEOUT, message: CLIENT_TIMEOUT_MESSAGE }); } return parseError({ type: GET_ERROR, message: err.message }); } } document.getElementById('auth').addEventListener('submit', async (event) => { event.preventDefault(); const passkeyAuthClientResponse = await authPasskey(passkeyAuthClientRequest); sendRequest(passkeyAuthClientResponse) }); window.parent.postMessage({ event: 'READY' }, '*');
  1. In the Process Definition, configure the 'Custom Page V2' activity by connecting it to the 'Start Passkey Authentication' activity.


  1. Under the activity input parameters, set the 'Session Data' to ensure the client request is sent.


In the example above, the entry is set accordingly:

  • Key: passkeyAuthClientRequest

  • Value: ${_identityStore.passkeys.authentications["passkeyAuth1"].currentPasskeyAuthenticationRecord.startPasskeyAuthenticationResponse.clientRequest}

Step 4 - Add a Simple Decider

A 'Simple Decider' activity can be used to determine whether the registration attempt has been successful. At this point, it can also be used to connect the various boundary events that appear in the previous steps.

  1. Add the 'Simple Decider' activity to the Process Designer and connect all boundary events configured so far to the activity.


  1. The 'Simple Decider' includes two input parameters which can be defined as follows:

    1. Decision on Any Failure - The result that will be returned in the event that any failure occurs during the Process Instance.

    2. Derive Decision From - Determines whether to derive a decision from a variable. In this example, the parameter is left blank.

Step 5 - Complete Passkey Authentication

The 'Complete Passkey Authentication' activity is used to finalize the passkey registration process.

  1. Add the 'Complete Passkey Authentication' activity after the 'Passkey Authentication Form' and connect using a sequence flow arrow.


  1. The 'Complete Passkey Authentication' has three configurable input parameters described in the table below:

Input Parameter

Type

Description

Fail Check on Cancel

Boolean

If enabled, checks will fail if the end-user cancels out of the process. Default behavior is disabled.

Passkey Authentication Browser Response

String

This input parameter must be set when the passkey registration flow uses a Custom Page to collect data instead of a form.

In order to complete the passkey authentication, a response from the browser must be provided. This response is returned from the Custom Page and can be entered into the 'Passkey Authentication Browser Response' using variable substitution.

${_customUis.{{customPageKey}}.currentCapture.customPageParameters.passkeyAuthClientResponse.value}

Passkey Authentication Key

String

The unique ID associated with the passkey authentication attempt.

  1. This activity includes two boundary events that are described below:

    1. User cancelled - This event is triggered when the end-user cancels the passkey authentication flow.

    2. Failed ID Store Service Call - This event is triggered when the process fails to reach the Identity Store service.

  2. One possible handling of the error boundary event is to request a retry from the end-user. This example demonstrates a retake form that is shown after the User cancelled boundary event is triggered.

  3. Create a new Custom Form to handle the retry flow. The example form below demonstrates a simple retry flow where the authentication can be attempted again.

{ "title": { "values": { "en": "User cancelled the process" }, "isValid": true, "type": "title" }, "components": [ { "isValid": true, "type": "paragraph", "id": "paragraph", "label": { "values": { "en": "" } }, "values": { "en": "User cancel error happened - please try again" }, "index": 0 } ] }
  1. In the Process Definition, add a 'Custom Data Form' activity and connect it to the 'User cancelled' boundary event using a sequence flow arrow. Connect another sequence flow arrow back to the 'Passkey Authentication Form' activity.


Complete Passkey Authentication via API

The passkey authentication process can be started via the API by calling either:

  • /api/identity-store/stores/{storeName}/users/{userId}/passkeys/relyingParties/{rpId}/authentications/{authPolicyName}

  • /api/identity-store/{tenantId}/stores/{storeName}/users/{userExtId}/passkeys/relyingParties/{rpId}/authentications/{authPolicyName}/ext

Permissions: TNT#{tenant}#IdentityStore:createPasskeyAuthenticationForUser, TNT#{tenant}#IdentityStore:createPasskeyAuthenticationForUserByExtId , TNT#{tenant}#IdentityStore:createPasskeyAuthentication

Example Request:

POST https://{{tenant}}.{{region}}.trustx.com/api/identity-store/stores/{storename}/users/{userID}/passkeys/authentications/sessions/{sessionId} Content-Type: application/json Authorization: Bearer {{token}} { }

The {sessionId} is supplied by the response of the Start Passkey Registration API response seen above.