UMFA APIs for React Native Applications
The UMFAClient enables secure, multi-factor authentication in React Native applications by managing user enrollment, authentication, and device management workflows. It abstracts the complexity of FIDO2 standards and provides flexible integration options for developers.
Key features include:
- Check Enrollment: Checks current enrollment status on the device.
- Enrollment: Associate a user’s identity with their device.
- Authentication: Validate the user using cryptographically secure challenges.
- Device Unbind: Unbind credentials for re-enrollment for this device and user.
Interface Definitions
Before proceeding, ensure you have imported the UMFAClient from the ZSM React Native SDK:
import { UMFAClient } from '@ideem/zsm-react-native';
Check Enrollment: UMFAClient.checkEnrollment()
This method verifies whether a user is already enrolled in the system.
Parameters
| Parameter Name | Data Type | Description |
|---|---|---|
userIdentifier | string | The username or unique user ID. |
Returns
- Boolean:
trueif the user is enrolled. false: If the user is not enrolled.- Error: Returns an error object if the operation fails.
Usage Example
client.checkEnrollment('exampleUser')
.then(isEnrolled => {
console.log(`Enrollment status: ${isEnrolled}`);
})
.catch(error => {
console.error('Error checking enrollment:', error.message);
});
Enroll: UMFAClient.enroll()
Enrolls a user by creating a credential and associating it with their device.
Parameters
| Parameter Name | Data Type | Description |
|---|---|---|
userIdentifier | string | The username or unique user ID. |
Returns
- String: The authentication token.
- Error: Returns an error object if enrollment fails.
Usage Example
client.enroll('exampleUser')
.then(token => {
console.log('Enrollment successful, token:', token);
})
.catch(error => {
console.error('Error during enrollment:', error.message);
});
Authenticate: UMFAClient.authenticate()
Authenticates a user by requesting a cryptographic challenge to be signed by the authenticator.
Parameters
| Parameter Name | Data Type | Description |
|---|---|---|
userIdentifier | string | The username or unique user ID. |
Returns
- Object: Authentication result, including a signed challenge.
- Error: Returns an error object if authentication fails.
Usage Example
client.authenticate('exampleUser')
.then(result => {
console.log('Authentication successful:', result);
})
.catch(error => {
console.error('Error during authentication:', error.message);
});
Unenroll UMFAClient.unenroll() (Remove user's enrollment from the device)
The unenroll method of the UMFAClient class removes the specified, enrolled user's credentials from the device the user is actively logged in with. This effectively deletes any credentials associated with the user, preventing future authentication using those credentials, until the user re-enrolls on said device.
Parameters
| Parameter Name | Data Type | Description |
|---|---|---|
userIdentifier | String | The unique identifier for the user. This is typically the user's email address or a UUID. |
Returns
| Parameter Name | Data Type | Description |
|---|---|---|
response | Promise | Returns a promise containing the results of the unenrollment. This can include any one of the following: |
true (boolean) : The user's credentials were successfully removed from the device | ||
false (boolean): The user was not enrolled and/or had no valid local credential set | ||
error ( Error ): Unable to unenroll |
Usage
JavaScript
const unenrollResult = await UMFAClient.unenroll(userIdentifier);
if (unenrollResult instanceof Error) throw(unenrollResult); // Error Condition
console.log(`${userIdentifier} successfully unenrolled!`); // Successful Unenrollment
Out-of-Band Token Validation
For out-of-band validation of the JWT returned by enroll and authenticate, use the /api/umfa/validate-token endpoint.
HTTP Method
POST
URL
$ZSM_AUTHENTICATOR_HOST/api/umfa/validate-token
Request Headers
| Header | Value | Description |
|---|---|---|
Content-Type | application/json | Indicates the payload format |
Authorization | Bearer XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX | Authorizes the API (API Key expected) |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
application_id | string | Yes | The unique identifier for the request's (server-to-server) application, parsed as a UUID. |
user_id | string | Yes | The unique identifier for the user. |
token | string | Yes | The token received from a previous UMFA authentication operation. |
token_type | string | No | An optional token type specifying the type of validation. Can be "credential" to validate a get() PublicKeyCredential. |
trace_id | string | No | An optional identifier for the validate-token operation. If a trace_id is not provided, then a random trace_id will be generated. |
Successful Response (HTTP 200)
{
"user_id": "janedoe@gmail.com",
"trace_id": "7a626fe9-ce25-4b87-8eb2-b12a7ee20143"
}
| Field | Type | Description |
|---|---|---|
user_id | string | The unique identifier for the user that was validated. |
trace_id | string | The trace identifier for the validate-token operation. |
Error Responses
{
"status": 400,
"trace_id": "7a626fe9-ce25-4b87-8eb2-b12a7ee20143",
"message": "Validate token failed with: MFA login JWT was invalid: Invalid JWT: There is no user_id claim"
}
| Field | Type | Description |
|---|---|---|
status | integer | The HTTP response code. |
trace_id | string | The trace identifier for the verification operation. |
message | string | Additional information about the validation failure. |
| Status Code | Description | Example Response |
|---|---|---|
400 Bad Request | Incorrectly formed request | { ... "message": "No data provided." } |
401 Unauthorized | Invalid or expired token | { ... "message": "Validate token failed with: MFA login JWT was invalid: Invalid JWT: There is no webauthn_time claim" } |
500 Internal Server Error | Server encountered an issue | { ... "message": "Server encountered an internal error" } |
Example cURL Commands
HTTP Success (200) JWT Validation
$ curl -s - X POST -H "Content-Type: application/json"
-H "Authorization: Bearer XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
-d '{"application_id": "bf468b21-308f-49d2-9031-83556e0781d2",
"user_id": "janedoe@gmail.com", "token": "eyJ0eX ... Nk9uWg"}'
$ZSM_AUTHENTICATOR_HOST/api/umfa/validate-token | jq
{
"user_id": "c7d7d44b-385e-4e83-bdd5-37e4fb3c8b7d",
"trace_id": "7a626fe9-ce25-4b87-8eb2-b12a7ee20143"
}
HTTP Success (200) Credential Validation
$ curl -s - X POST -H "Content-Type: application/json"
-H "Authorization: Bearer XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
-d '{"application_id": "bf468b21-308f-49d2-9031-83556e0781d2",
"user_id": "janedoe@gmail.com",
"token":
{
"id":"",
"rawId":"rF2kHiKUQCO0d0Y4Wek9kA",
"response":
{
"clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiNWtMb2tjOXpkZzhuU2RFT2hzV1o5MUwzZTNGdjlqbERlRU9KcF93SnRkayIsIm9yaWdpbiI6Imh0dHBzOi8venNtLmFwcCJ9","authenticatorData":"ZxcNEwlh6TBKTTC5FMSFPTOboOZWzGeOSiYY7rm67WUFAAAAAQ","signature":"UOOoSBAwemLSPvqLVG2MDw41cqKLcHEUp4LAFGuvrVPsR1GWBYTWtmpqG_Jtjn-DXq5tGGAE58SYvu7uvcw7oHqquoNG4VEdm4Tz7UNe5kdSoc3RFpEGGDLCIz28iKXaBPbv3jdHi4xGoCIJKIIeHyh0-g7LUb4ZjYFIZHyXds7cdH9ozXRt5ERWUVvH1axDnPDKpntGQXG8FC4VXd0Rc01-4bBklNSGHOVgbO-Rpm8HgeFj3J4uOZDJ0xP7pnIkwOo5Uw_0ZO9xI66S8NQEtVzXVUKXs98f38LpLiLGEPlWtr_RIdf9xgsmHx-oVRJxC37gzV2ydSGKJaV6bNsXOw"},
"type":"public-key"
}
},
"token-type": "credential"}'
$ZSM_AUTHENTICATOR_HOST/api/umfa/validate-token | jq
{
"user_id": "c7d7d44b-385e-4e83-bdd5-37e4fb3c8b7d",
"trace_id": "7a626fe9-ce25-4b87-8eb2-b12a7ee20143"
}
Bad Request (400)
$ curl -s - X POST -H "Content-Type: application/json"
-H "Authorization: Bearer XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
-d '{"application_id": "bf468b21-308f-49d2-9031-83556e0781d2",
"user_id": "janedoe@gmail.com"}'
$ZSM_AUTHENTICATOR_HOST/api/umfa/validate-token | jq
{
"status": 400,
"trace_id": "7a626fe9-ce25-4b87-8eb2-b12a7ee20143",
"message": "Invalid data provided"
}
Unauthorized (401)
$ curl -s - X POST -H "Content-Type: application/json"
-H "Authorization: Bearer XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
-d '{"application_id": "bf468b21-308f-49d2-9031-83556e0781d2",
"user_id": "janedoe@gmail.com", "token": "eyJ0eX ... Nk9uWg"}'
$ZSM_AUTHENTICATOR_HOST/api/umfa/validate-token | jq
{
"status": 401,
"trace_id": "7a626fe9-ce25-4b87-8eb2-b12a7ee20143",
"message": "Validate token failed with: MFA login JWT was invalid: Invalid JWT: Invalid claim: The token has expired: 2024-12-10 18:41:03.0 +00:00:00"
}
Validate Token Failures
The server's token validation can fail for two general reasons:
- HTTP 400 Response: The request was malformed (i.e.,
tokenwas not included) - HTTP 401 Response: The token was invalid, which could have various causes
- Token's
user_iddid not match the supplieduser_id - Token has expired (the token's "exp" claim has passed)
- Token was missing required claims ("iss", "sub", "iat", "exp", "user_id", "webauthn_time")
- Token was not signed by the expected ZSM server's certificate
- Token was not a valid PublicKeyCredential when supplied
"token_type" = "credential"
- Token's
Decoded JWT
Below, we illustrate an example of a decoded UMFA JWT (Header and Payload).
The validation performs standard JWT validation like signature, liveness,
and structure. The validation also ensures that the payload claim user_id
matches that of the supplied user_id.
{
"typ": "JWT",
"alg": "RS256",
"iss": "Ideem::Authenticator"
}
{
"sub": "UMFA_login",
"iss": "Ideem::Authenticator",
"aud": [
"Ideem::Authenticator",
"Ideem::ZSM",
"Ideem::ZSM_CLI"
],
"iat": 1729280408,
"exp": 1729366808,
"jti": "042142c1-40c8-4d9b-bf5e-1fa84e0f3f03",
"user_id": "c7d7d44b-385e-4e83-bdd5-37e4fb3c8b7d",
"userpw_time": "2024-10-18T19:40:07.053364351+00:00",
"webauthn_time": "2024-10-18T19:40:08.053364351+00:00"
}
Example Code
Default Flow: Enrollment and Authentication
Below is an example implementation of the default UMFA flow for user enrollment and authentication:
import { UMFAClient } from 'zsm-react-native';
const config = {
application_id: 'a1f4769a-a4be-45f9-91af-f3568f054be9',
host_url: 'https://zsm-authenticator-demo.useideem.com/',
application_environment: 'TEST',
api_key: '33214443-c760-4f8e-924e-9a2ad5cb0bf6',
};
const client = new UMFAClient(config);
const username = 'exampleUser';
// Enrollment and Authentication Flow
async function loginFlow() {
try {
const isEnrolled = await client.checkEnrollment(username);
if (!isEnrolled) {
console.log('User not enrolled. Enrolling...');
const token = await client.enroll(username);
console.log('Enrollment successful. Token:', token);
}
const authResult = await client.authenticate(username);
console.log('Authentication successful:', authResult);
} catch (error) {
console.error('Error in login flow:', error.message);
}
}
loginFlow();
Explanation
The UMFAClient simplifies the integration of multi-factor authentication by abstracting the complexities of enrollment, authentication, and device management:
- Check Enrollment: The
checkEnrollment()method checks to see if the user is already enrolled on their device. - Enrollment: The
enroll()method registers a user’s identity and associates it with their device. - Authentication: The
authenticate()method validates the user with cryptographically signed challenges. - Enrollment Management: The
unenroll()method clears credentials, allowing re-enrollment in specific scenarios. - Flexibility: Developers can use the default setup or extend it with a custom Relying Party to manage backend workflows.
Summary
The UMFAClient provides a robust and flexible API for integrating multi-factor authentication into React Native applications. Whether using the default configuration or implementing custom workflows, developers can ensure secure and reliable authentication with minimal effort.