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 NameData TypeDescription
userIdentifierstringThe username or unique user ID.

Returns

  • Boolean: true if 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 NameData TypeDescription
userIdentifierstringThe 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 NameData TypeDescription
userIdentifierstringThe 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 NameData TypeDescription
userIdentifierStringThe unique identifier for the user. This is typically the user's email address or a UUID.

Returns

Parameter NameData TypeDescription
responsePromiseReturns 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 's identity from this device.

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

HeaderValueDescription
Content-Typeapplication/jsonIndicates the payload format
AuthorizationBearer XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXAuthorizes the API (API Key expected)

Request Body

FieldTypeRequiredDescription
application_idstringYesThe unique identifier for the request's (server-to-server) application, parsed as a UUID.
user_idstringYesThe unique identifier for the user.
tokenstringYesThe token received from a previous UMFA authentication operation.
token_typestringNoAn optional token type specifying the type of validation. Can be "credential" to validate a get() PublicKeyCredential.
trace_idstringNoAn 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"
}
FieldTypeDescription
user_idstringThe unique identifier for the user that was validated.
trace_idstringThe 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"
}
FieldTypeDescription
statusintegerThe HTTP response code.
trace_idstringThe trace identifier for the verification operation.
messagestringAdditional information about the validation failure.
Status CodeDescriptionExample Response
400 Bad RequestIncorrectly formed request{ ... "message": "No data provided." }
401 UnauthorizedInvalid or expired token{ ... "message": "Validate token failed with: MFA login JWT was invalid: Invalid JWT: There is no webauthn_time claim" }
500 Internal Server ErrorServer 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., token was not included)
  • HTTP 401 Response: The token was invalid, which could have various causes
    • Token's user_id did not match the supplied user_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"

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:

  1. Check Enrollment: The checkEnrollment() method checks to see if the user is already enrolled on their device.
  2. Enrollment: The enroll() method registers a user’s identity and associates it with their device.
  3. Authentication: The authenticate() method validates the user with cryptographically signed challenges.
  4. Enrollment Management: The unenroll() method clears credentials, allowing re-enrollment in specific scenarios.
  5. 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.