---
title: Mobile SDK JWT Signing
description: Learn how to generate a signed JWT with the PingOne Recognize Mobile SDK.
component: recognize
page_id: recognize:mobile-sdk:mobile-sdk-jwt-signing
canonical_url: https://docs.pingidentity.com/recognize/mobile-sdk/mobile-sdk-jwt-signing.html
llms_txt: https://docs.pingidentity.com/recognize/llms.txt
docs_for_agents: https://developer.pingidentity.com/build-with-ai/docs-for-agents.md
revdate: April 20, 2026
section_ids:
  generate-signed-jwt: Generate Signed JWT
  android: Android
  ios: iOS
  flutter: Flutter
  react-native: React Native
  user-signing-public-key: User Signing Public Key
  jwt-verification-best-practice: JWT Verification Best Practice
  where-to-download-the-pingone-recognize-jwks-json-endpoint: Where to Download the PingOne Recognize jwks.json Endpoint
  the-verification-process: The Verification Process
  handling-key-rotation: Handling Key Rotation
---

# Mobile SDK JWT Signing

PingOne Recognize Mobile SDK can generate a signed JWT containing a custom payload. You can use the signed JWT to implement [Dynamic Linking](mobile-sdk-dynamic-linking.html).

## Generate Signed JWT

Pass `JwtSigningInfo` to the authentication configuration to generate a signed JWT.

### Android

```kotlin
// {p1recognize} adds a td claim to the JWTs containing the data you specify
val jwtSigningInfo = JwtSigningInfo(claimTransactionData = "<your custom data>")

// If you want to authenticate with biometric
val biomAuthConfig = BiomAuthConfig(jwtSigningInfo = jwtSigningInfo)
// If you want to authenticate with pin
val pinAuthConfig = PinAuthConfig(pin = "1234", jwtSigningInfo = jwtSigningInfo)

// Perform the authentication
Keyless.authenticate(
    configuration = biomAuthConfig, // pinAuthConfig if you use pin
    onCompletion = { /* TODO: process result */ }
)
```

### iOS

```swift
// {p1recognize} adds a td claim to the JWTs containing the data you specify
let jwtSigningInfo = JwtSigningInfo(claimTransactionData: "<your custom data>")

// If you want to authenticate with biometric
let biomAuthConfig = BiomAuthConfig(jwtSigningInfo: jwtSigningInfo)
// If you want to authenticate with pin
let pinAuthConfig = PinAuthConfig(pin: "1234", jwtSigningInfo: jwtSigningInfo)

// Perform the authentication
Keyless.authenticate(
    configuration: biomAuthConfig, // pinAuthConfig if you use pin
    onCompletion: { /* TODO: process result */ }
)
```

### Flutter

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

// {p1recognize} adds a td claim to the JWTs containing the data you specify
final jwtSigningInfo = JwtSigningInfo("<your custom data>");

// If you want to authenticate with biometric
final biomAuthConfig = BiomAuthConfig(
  jwtSigningInfo: jwtSigningInfo,
);

// If you want to authenticate with pin
final pinAuthConfig = PinAuthConfig(
  pin: "1234",
  jwtSigningInfo: jwtSigningInfo,
);

try {
  // Perform the authentication with either biometric or pin config
  final result = await Keyless.instance.authenticate(
    biomAuthConfig, // or pinAuthConfig if you use pin
  );
  print("JWT signed successfully: ${result.signedJwt}");
} catch (error) {
  print("Authentication failed: $error");
}
```

### React Native

```text
// {p1recognize} adds a td claim to the JWTs containing the data you specify
const jwtSigningInfo = new JwtSigningInfo('<your custom data>');

// If you want to authenticate with biometric
const biomAuthConfig = new BiomAuthConfig({
  jwtSigningInfo: jwtSigningInfo,
});

// If you want to authenticate with pin
const pinAuthConfig = new PinAuthConfig({
  pin: '1234',
  jwtSigningInfo: jwtSigningInfo,
});

const result = await Keyless.authenticate(
  biomAuthConfig, // or pinAuthConfig if you use pin
);

result.fold({
  onSuccess(data) {
    logConsole('Auth result success ' + JSON.stringify(data, null, 2));
  },
  onFailure(error) {
    logConsole('Auth result failure ' + JSON.stringify(error, null, 2));
  },
});
```

## User Signing Public Key

`AuthenticationSuccess` contains the following field:

* `signedJwt`: the signed JWT.

## JWT Verification Best Practice

Follow best practice when implementing JWT verification. This also allows KMS key rotation and improves SaaS platform resilience (for example, active/active implementations).

### Where to Download the PingOne Recognize `jwks.json` Endpoint

The endpoint for downloading `jwks.json` is:

<https://api.keyless.io/customers/keyless/.well-known/jwks.json>

|   |                                                                                                                                                                                                                                                                                          |
| - | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | This example uses `api.keyless.io` as the Operation Service URL and `keyless` as the customer name. Replace the customer name with your tenant name and use the correct regional Operation Service URL listed [here](https://docs.keyless.io/consumer/server-api/getting-started#latam). |

### The Verification Process

1. **Extract the Header:** Parse the JWT header (unverified) to retrieve `kid` (Key ID) and `alg` (algorithm).

2. **Lookup the Key:** Search the cached `jwks.json` for a key where `kid` matches the JWT header. Confirm `use` is `sig` and `alg` matches your expected security profile (for example, `RS256`).

3. **Construct Public Key:** Convert JWK components (for RSA: `n` modulus and `e` exponent) into a PEM-formatted public key.

4. **Validate Signature:** Use the reconstructed public key to verify the JWT cryptographic signature and check standard claims (`exp`, `iat`, `iss`).

### Handling Key Rotation

To prevent authentication failures when keys rotate, implement the following validation logic:

1. Caching with Refresh: Maintain an in-memory cache of the JWKS and set a standard TTL (for example, 24 hours).

2. Lazy Refresh on `kid` Mismatch: If a JWT arrives with a `kid` not present in the current cache, perform an immediate one-time fetch of `jwks.json` to check if a new key is published.

3. Rate Limiting: Limit on-demand JWKS fetches (for example, once every 5 minutes) to prevent abuse from random `kid` token spam (DoS risk).

4. Graceful Overlap: Ensure verification logic handles multiple keys in the `keys` array simultaneously. During rotation, providers publish both old and new keys; trust any currently valid key in the set.
