Transaction Signing with Passkeys

Prompts the user to approve a financial transaction using Passkeys

Description

This step allows the user to provide consent for a transaction by authenticating using WebAuthn biometrics, which serves as their digital signature. The biometric credentials must first be registered in a separate journey using the Register Passkeys step.

Key use cases for this step include approving financial transactions (e.g., payments or money transfers), authorizing sensitive actions requiring explicit user consent, and enhancing transaction security while maintaining a frictionless user experience.

The transaction signing process begins when the client initiates the journey by sending approvalData (a flat JSON object containing transaction details, such as amount, recipient, and currency) to the SDK. The SDK processes the approvalData and provides instructions for the client to present the transaction details to the user for consent.

At this stage, the Transaction Signing with Passkeys step instructs the client to authenticate the user with their passkey credentials. This process is handled by the Platform SDK (Web), Authentication SDK (iOS), or Authentication SDK (Android). The SDK presents the transaction details to the user and prompts them for biometrics (e.g., fingerprint, facial recognition). If biometrics are unavailable, the operating system may suggest an alternative authentication method, such as a PIN or security key. The user consents to the transaction by authenticating, effectively signing the transaction with their passkey.

If validation succeeds, Mosaic validates the signed result (encoded_result) against the provided approvalData. This ensures that the signed transaction matches the original transaction details (e.g., amount, recipient, currency). Upon successful validation, Mosaic:

  • Issues tokens that include the signed transaction as claims, which can be used in downstream processes for audit or authorization.
  • Stores the signed result in the output variable specified in the configuration, making it accessible for subsequent steps.
  • Proceeds to the next journey step, allowing the journey to continue as defined in its configuration.

If validation fails (e.g., due to an invalid passkey or timeout), Mosaic rejects the signed result and redirects the journey to a failure branch or aborts it. The client can implement fallback mechanisms, such as retrying the transaction signing process or notifying the user with a “Try Again” prompt.

This step is available for both web and mobile (iOS, Android) SDKs.

Configuration

Field Description
External user ID Expression to retrieve the user’s identifier (e.g., email, username, or phone number) from the journey context or custom query parameters:
  • If the user has already authenticated in the journey, you can extract the external user ID from the journey context by configuring the External User ID field with the @policy.userContext().external_user_id expression.
  • If the user has not authenticated in the journey, an external user ID must CAN be passed as a custom query parameter in the start journey call (see Web, iOS, Android), or collected from the user using a form ( see Collect information). To retrieve the ID from the query parameters, configure the External User ID field to use the @policy.request().params.external_user_id expression.
    Approval data A JSON object containing a flat list of up to 10-string properties used to derive the Passkey challenge. Each value must be a string. Allowed characters: alphanumeric, spaces, and the special characters . , _ and -. This object is dynamically generated based on transaction details, such as amount, currency, and transaction ID.
    Output variable The variable to store the result of the transaction signing step. This variable can be used in subsequent steps to verify the transaction status or perform additional actions.
    Error output variable The variable to store errors returned by the step, such as failed authentication or invalid passkey selection.
    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: Approving a banking transaction

    A user of an online banking application needs to approve a payment transaction of $1,000 to “Merchant A.” The transaction details—amount, recipient, and currency—are displayed to the user for consent. The user authenticates using a passkey, such as biometrics or a PIN, to securely sign the transaction.

    The client initiates the transaction signing journey by sending approvalData to Mosaic IDO. The approvalData contains the transaction details and is used to derive the Passkey challenge for the user.

    Copy
    Copied
    {
      "amount": "1000",
      "currency": "USD",
      "recipient": "Merchant A",
      "transaction_id": "txn_12345"
    }

    Mosaic processes the journey and instructs the client to collect user consent via the SDK. The SDK presents the transaction details to the user and prompts them for biometric authentication. Upon successful authentication, the SDK returns an encoded_result, representing the signed transaction.

    The following examples demonstrate how to use the SDK to initiate the transaction signing process and handle the user’s Passkey authentication response:

    JavaScriptSwiftKotlin
    Copy
    Copied
    function handleTransactionApproveWithPasskey(idoResponse) {
      // Extract approval data from the journey response
      const approvalData = idoResponse.data.approval_data;
    
      // Trigger WebAuthn approval with Passkey using the extracted approval data
      window.tsPlatform.webauthn.approve
        .modal(null, approvalData)
        .then((webauthnEncodedResult) => {
          // Submit successful WebAuthn result back to Mosaic
          window.tsPlatform.ido.submitClientResponse(
            ClientResponseOptionType.ClientInput,
            webauthnEncodedResult
          );
        })
        .catch((error) => {
          // Handle WebAuthn approval failure and submit error response back to Mosaic
          window.tsPlatform.ido.submitClientResponse(
            ClientResponseOptionType.Fail,
            error
          );
        });
    }
    Copy
    Copied
    // Extract user ID and approval data from the IDO response
    let userId = idoResponse.data[“user_identifier”] as? String
    let approvalData = idoResponse.data[“approval_data”] as? [String: Any]
    
    // Initiate WebAuthn approval with Passkey
    TSAuthentication.shared.approvalWebAuthn(approvalData: approvalData, username: nil) { response in
    switch response {
    case .success(let result):
    // Handle successful WebAuthn approval and log the result
    debugPrint([DEBUG]: WebAuthn approval success with result: (result.result))
    case .failure(let error):
    // Handle WebAuthn approval failure and log the error
    debugPrint([DEBUG]: WebAuthn approval failed with error: (error.message))
    }
    }
    Copy
    Copied
    private fun signTransactionWebAuthn(idoResponse: IdoResponse) {
        // Extract approval data from the IDO response
        val approvalData = idoResponse.responseData?.optJSONObject("approval_data")?.toMap()
    
        // Initiate WebAuthn approval with Passkey
        TSAuthentication.approvalWebAuthn(context, null, approvalData, object :
            TSAuthCallback<TSWebAuthnApprovalResult, TSWebAuthnApprovalError> {
    
            override fun error(error: TSWebAuthnApprovalError) {
                // Handle WebAuthn approval failure and display an error message
                showError("WebAuthn transaction signing error: " + error.message)
            }
    
            override fun success(approvalResult: TSWebAuthnApprovalResult) {
                // Handle successful WebAuthn approval and log the encoded result
                Log.d(TAG, "WebAuthn transaction signing success, encoded result:" + approvalResult.result())
            }
        })
    }

    The client sends the encoded_result to Mosaic for validation.