PingOne Recognize

Dynamic Linking

You can use the PingOne Recognize authentication mechanism to sign unrelated transactions, including Strong Customer Authentication (SCA) transactions.

Payment service providers compliant with SCA PSD2 dynamic linking are required to:

  • Generate an authentication code specific to the amount of the payment transaction and the payee agreed to by the payer when initiating the transaction.

  • Make the payer aware of the amount of the payment transaction and of the payee.

PingOne Recognize helps by:

  • Protecting the authentication code used for dynamic linking.

  • Displaying and signing information to make the payer aware of transaction details.

PingOne Recognize is not a payment service provider. PingOne Recognize does not issue an authentication code tied to transaction information.

Dynamic Linking Flow

sequenceDiagram
    participant Customer backend
    participant Customer app
    participant {p1recognize} SDK
    participant {p1recognize} API
    Customer app->>{p1recognize} SDK: Keyless.Authenticate(transaction details)
    {p1recognize} SDK->>{p1recognize} SDK: User reviews transaction details<br/>and approves transaction
    note over {p1recognize} SDK,{p1recognize} API: {p1recognize} authentication happens<br/>{p1recognize} signs transaction details
    {p1recognize} SDK->>Customer app: KeylessResult includes<br/>a signed JWT with transaction details
    Customer app->>Customer backend: Customer app sends<br/>signed JWT to backend
    Customer backend->>Customer backend: Backend validates signature

Strong Customer Authentication (SCA)

By adding PingOne Recognize to your checkout flow, you also benefit from PingOne Recognize Passwordless Multi-Factor Authentication (MFA).

SCA requires authentication to use at least two of the following three elements:

  • Something the customer knows (for example, password or PIN)

  • Something the customer has (for example, mobile phone or hardware token)

  • Something the customer is (for example, biometric such as fingerprint or face)

With PingOne Recognize Passwordless MFA, you can satisfy the last two points in the list above.

SCA-Compliant Dynamic Linking

The following sections show examples of implementing SCA with PingOne Recognize.

Display Transaction Information

PingOne Recognize displays a screen containing labels and associated information on your behalf.

For this reason, dynamicLinkingInfo must be a JSON array containing JSON objects (key/value pairs). Expected format:

[
  {"key1": "value1"},
  {"key2": "value2"},
  ...,
  {"keyN": "valueN"}
]

This information is added to the authentication request the user must approve.

SCA Tied to the Authentication Code

After the user approves transaction data, PingOne Recognize starts authentication to:

  1. Authenticate the payer with device factor and biometric factor using PingOne Recognize MFA.

  2. Tie transaction data to PingOne Recognize MFA.

To tie transaction data to PingOne Recognize MFA, populate dynamicLinkingInfo in AuthConfig. Add the authentication code or other information you want to display to the user and sign with PingOne Recognize MFA.

The transactionData in dynamicLinkingInfo must follow the format described in Display Transaction Information.

PingOne Recognize can produce a signed JWT containing a claim titled td (transaction data), which contains the payload passed as dynamicLinkingInfo.

PingOne Recognize does not store transaction history records for amount, payee, payer, or authentication code.

Android

// {p1recognize} adds a td claim to JWT containing the data you specify
val dynamicLinkingInfo = DynamicLinkingInfo(transactionData = "<your transaction data to display and sign>")

// Authenticate with biometric
val biomAuthConfig = BiomAuthConfig(dynamicLinkingInfo = dynamicLinkingInfo)

// Perform authentication
Keyless.authenticate(
    configuration = biomAuthConfig,
    onCompletion = { /* TODO: process result */ }
)

iOS

// {p1recognize} adds a td claim to JWT containing the data you specify
let dynamicLinkingInfo = DynamicLinkingInfo(transactionData = "<your transaction data to display and sign>")

// Authenticate with biometric
let biomAuthConfig = BiomAuthConfig(dynamicLinkingInfo: dynamicLinkingInfo)

// Perform authentication
Keyless.authenticate(
    configuration: biomAuthConfig,
    onCompletion: { /* TODO: process result */ }
)

Flutter

import 'package:keyless_flutter_sdk/keyless.dart';
import 'package:keyless_flutter_sdk/models/configurations/authentication_configuration.dart';
import 'package:keyless_flutter_sdk/models/dynamic_linking_info.dart';

final dynamicLinkingInfo = DynamicLinkingInfo(
  transactionData: "<your transaction data to display and sign>",
);

// Authenticate with biometric
final biomAuthConfig = BiomAuthConfig(dynamicLinkingInfo: dynamicLinkingInfo);

try {
  // Perform authentication
  final result = await Keyless.instance.authenticate(biomAuthConfig);
  // Process signed JWT from result.signedJwt
  print("Authentication successful. Signed JWT: ${result.signedJwt}");
} catch (error) {
  print("Authentication failed: $error");
}

Verify the Transaction

If authentication succeeds, AuthenticationSuccess includes:

  • signedJwt: the signed JWT

// JWT header
{
  "alg": "ES256",
  "typ": "JWT",
  "kid": "PIN/FACE"
}

// JWT payload
{
  "iat": 1720519812,
  "td": "your transaction data to display and sign",
  "version": "1.1.0",
  "sub": "keyless_id",
  "external_user_id": "external user id only if present"
}
The external_user_id claim is included only if the user requesting the JWT has an associated external user ID.

Verify the JWT using the public key from PingOne Recognize backend operations API.

You have now performed a Strong Customer Authentication flow that displays and signs transaction information.