Passkey Registration

Early Access

Introduction

To construct a simple experience, registration is controlled using a Custom Form or Custom Page within TrustX. This document will describe the steps in configuring a solution using the Process Designer and TrustX API to register a new User using passkey registration.

This guide will use a Custom Form that requests a User name and User display name to register a User in an Identity Store.

Info

Before a User can be registered a passkey policy must be configured. See the Managing Policies guide for more information on configuring a passkey policy.

Registering via Process Definition

The example Process Definition in this guide will utilize a Custom Data Form to handle the registration of a passkey.

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 - Add a User (Optional)

This step is only required if the User has not been added to the Identity Store. See Managing Users for more information on adding a User.

  1. The 'Add User' activity is used to add a new User to an Identity Store. Find the activity and connect it to the 'Start' and 'End' events using a sequence flow arrow.


  1. The 'Add User' activity includes two error events. In this example, the error events have been connected to the 'End' event.

    1. Failed to add the user - This event is triggered when an error occurs adding the User to the Identity Store.

    2. ExtId Not Unique - This event occurs if the External ID provided is not unique.

  2. Click the 'Add User' activity to open the right-side contextual menu. Using the available input parameters, information about the User can be defined, including the External ID and which Identity Store the user will be added to.


  1. When creating a User, the following input parameters are available:

Input Parameter

Type

Description

Store Name*

String

Required - Represents the Identity Store that the User will be added to.

User Attributes

<String, String>

A key-value pair of additional optional attributes used to describe the User.

User Date of Birth

Date

The User's date of birth in the format: "yyyy-mm-dd"

User Email

String

The User's email address in the format: "example@domain.com"

User External ID

String

A unique User ID. If no ID is required, an external ID will be generated automatically by TrustX.

User First Name

String

The first name of the User.

User Key

String

Required - A key used to identify the User.

User Last Name

String

The last name of the User.

User Locale - Country

String

The User country.

User Locale - Language

String

A two-letter ISO-639 formatted language tag.

User Locale - Variant

String

A variant subtag of the User country. 5-8 letters or 4 characters starting with a digit, separated by hyphens.

User Nationalities

String

The nationality of the User.

User Status

String

The User status indicates whether the User is in a pending, active, locked or disabled state.

User Timezone

String

The timezone of the User Country. This value must be in line with TZDB code format.

Example: EST.

User Type

String

An optional parameter used for organizing Users into different types. A maximum of 64 characters is allowed.

Step 3 - Start Passkey Registration

  1. Add the 'Start Passkey Registration' activity to the Process Designer and connect the activity to the 'Add User' activity using a sequence flow arrow.


  1. The 'Start Passkey Registration' activity contains 12 configurable input parameters. The table below describes each parameter, which is accessed by clicking the activity and opening the contextual menu on the right of the screen.

Input Parameter

Type

Description

Challenge

String

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

Create User

Boolean

If enabled, a new User will be created as part of the passkey registration. Default behavior is disabled.

Passkey Registration Key

String

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

Passkey User Display Name

String

An informal display name associated with the User.

Passkey User Handle

String (Base-64)

A unique identifier that is used to identify the credentials of the User per relying party. This value is a base-64 encoded string of bytes with a maximum length of 64 bytes.

Passkey User Name

String

A unique Username to identify the User.

Registration Policy

String

Required - The name of the registration policy.

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

Required - If the 'Create User' property is enabled, this property must be configured as a unique ID for the newly created User.

Optional - If the 'Create User' property is disabled, 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 registration applies to.

Start Passkey Registration via API

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

  • /api/identity-store/stores/{storeName}/users/{userId}/passkeys/relyingParties/{rpId}/registrations/{regPolicyName}

  • /api/identity-store/stores/{storeName}/users/{userExtId}/passkeys/relyingParties/{rpId}/registrations/{regPolicyName}/ext

Permissions: TNT#{tenant}#IdentityStore:createPasskeyRegistrationForUser, TNT#{tenant}#IdentityStore:createPasskeyRegistrationForUserByExtId , TNT#{tenant}#IdentityStore:createPasskeyRegistration

Example Request:

POST https://{{tenant}}.{{region}}.trustx.com/api/identity-store/stores/{storename}/users/{userID}/passkeys/relyingParties/{rpId}/registrations/{regPolicyName} 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 4 - Configure the Registration Form

The example in this document will use a Custom Form that collects the 'Passkey User Display Name' and 'PassKey User Name' from the end-user. This Custom Form is then populated in the 'Passkey Registration Form' activity. This is a unique type of form activity that is used for the purpose of passkey registration.

  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": "Title" }, "isValid": true, "type": "title" }, "components": [ { "type": "text", "isValid": true, "readOnly": false, "id": "name", "label": { "values": { "en": "User Name" } }, "default": "", "transient": false, "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": false, "validate": { "required": true, "minLength": "", "maxLength": "", "missingMessage": { "values": { "en": "" } }, "invalidMessage": { "values": { "en": "" } }, "invalidLengthMessage": { "values": { "en": "" } } }, "index": 1 } ], "continueButton": { "isValid": true, "type": "continueButton", "values": { "en": "Continue" } } }
  1. Once the Custom Form has been created, add a 'Passkey Registration Form' activity to the Process Designer and connect to the 'Start Passkey Registration' activity using a sequence flow arrow.


  1. The 'Passkey Registration Form' contains configurable input parameters. The table below describes each configurable parameter:

Input Parameter

Type

Description

Data form name

String

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

Data form version

String

The version of the Custom Form.

Delay (in seconds)

Integer

When 'Hide buttons' is enabled, this value will determine how long the form will display before automatically proceeding 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 Registration Key

String

The unique ID associated with the passkey registration attempt.

Registration timeout (in seconds)

Integer

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

  1. When the form is displayed to the end-user, they will have the ability to define their username and display name:

The Registration Form in TrustWeb
  1. When clicking the 'Submit for Registration' button, a popup will appear to choose the registration method.


Step 4 - 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. For this flow to work, it is important that the 'Passkey Registration Browser Response' input parameter is entered in Step 6.

  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>Register 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">Register</button> </form> <script type="module" src="app.js"></script> </body> </html>
import { create, parseCreationOptionsFromJSON } from "https://cdn.jsdelivr.net/npm/@github/webauthn-json@2.0.2/dist/esm/webauthn-json.browser-ponyfill.js"; const CREATE_ERROR = 'CREATE_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 passkeyRegClientRequest; function sendRequest(passkeyRegClientResponse) { if (window.parent !== window) { window.parent.postMessage({ event: 'SEND', variables: { passkeyRegClientResponse: JSON.stringify(passkeyRegClientResponse) } }, '*'); } }; function parseError({ type, message }) { return { clientError: { errorType: type, errorMessage: message } }; }; const controller = new AbortController(); window.addEventListener('message', function (event) { if (event.data.variables) { const variables = event.data.variables; const constants = variables.constants; const sessionData = variables.sessionData; if (sessionData.passkeyRegClientRequest) { passkeyRegClientRequest = sessionData.passkeyRegClientRequest; } if (constants.timeoutInSeconds) { setTimeout(async () => { controller.abort() }, (constants.timeoutInSeconds - 12) * 1000) } } else { console.log('No variables received'); } }); async function registerPasskey(passkeyRegClientRequestUpdated) { let serverPublicKeyCredential; try { serverPublicKeyCredential = parseCreationOptionsFromJSON({ publicKey: { ...passkeyRegClientRequestUpdated } }) } catch (err) { return parseError({ type: CONFIGURATION_ERROR, message: err.message }); } try { const signal = controller.signal; const response = await create({ ...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: CREATE_ERROR, message: err.message }); } } document.getElementById('registration').addEventListener('submit', async (event) => { event.preventDefault(); const name = document.getElementById('name').value; const displayName = document.getElementById('displayName').value; const user = { ...passkeyRegClientRequest.user, name, displayName } const passkeyRegClientResponse = await registerPasskey({ ...passkeyRegClientRequest, user }); sendRequest(passkeyRegClientResponse) }); window.parent.postMessage({ event: 'READY' }, '*');
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; }
  1. In the Process Definition, configure the 'Custom Page V2' activity by connecting it to the 'Start Passkey Registration' activity.


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


In the example above, the entry is set accordingly:

  • Key: passkeyRegClientRequest

  • Value: ${_identityStore.passkeys.registrations["passkeyReg1"].currentPasskeyRegistrationRecord.startPasskeyRegistrationResponse.clientRequest}

Step 5 - 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 6 - Complete Passkey Registration

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

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


  1. The 'Complete Passkey Registration' 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 Registration 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 registration, a response from the browser must be provided. This response is returned from the Custom Page and can be entered into the 'Passkey Registration Browser Response' using variable substitution.

${_customUis.{{customPageKey}}``.currentCapture.customPageParameters.passkeyRegClientResponse.value}

For example:

${_customUis.customPage1.currentCapture.customPageParameters.passkeyRegClientResponse.value}

Passkey Registration Key

String

The unique ID associated with the passkey registration 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 registration 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 registration 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 Registration Form' activity.


Complete Passkey Registration via API

The passkey registration process can be completed via the API by calling either:

  • /api/identity-store/stores/{storeName}/users/{userId}/passkeys/relyingParties/{rpId}/registrations/{regPolicyName}

  • /api/identity-store/stores/{storeName}/users/{userExtId}/passkeys/relyingParties/{rpId}/registrations/{regPolicyName}/ext endpoints.

Permissions: TNT#{tenant}#IdentityStore:createPasskeyRegistrationForUser, TNT#{tenant}#IdentityStore:createPasskeyRegistrationForUserByExtId , TNT#{tenant}#IdentityStore:createPasskeyRegistration

Example Request:

POST https://{{tenant}}.{{region}}.trustx.com/api/identity-store/stores/{storename}/users/{userID}/passkeys/registrations/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.