UMFA APIs for Native Android Applications

Ideem's Universal MFA interfaces are used to integrate secure mutli-factor authentication into your application. The provided interfaces allow you to determine if a user has already been enrolled (cryptographically bound) to a device, create an enrollment if they have not, or authenticate them if they have. Enroll and authenticate operations provide a token upon successful completion, and that token must be passed back to customer's application server, and validated through a server-to-server API call to Ideem's infrastructure.

UMFA functions are used to provide a silent second factor after strong authentication, as well as reverification when needed for transactions requiring step up authentication.

Interface Definitions

Jump to: Kotlin, Java, Token Validation

Getting the version number of the SDK library: UMFAClient.versionString

To retrieve the version number of the SDK library use the class property UMFAClient.versionString. It will return the version number in semantic versioning format, major.minor.patch.

Kotlin

Check Enrollment: UMFAClient.checkEnrollment()

The checkEnrollment method checks if a user has previously enrolled in the Universal Multi-Factor Authentication (UMFA) process on the current device. This allows the application to determine whether to proceed with enrollment or authentication flows.

Usage

client.checkEnrollment(userId) { response, error ->
    if (response != null) {
        val rawId = response.getString("rawId")
        // Handle retrieved enrollment data
    } else {
        Log.e("UMFAClient", "Error retrieving enrollment data: ${error?.localizedMessage}")
    }
}

Parameters

Parameter NameData TypeDescription
userIdStringThe user ID to check enrollment for.
completion(JSONObject?, ZSMError?) -> UnitA callback function that handles the response.

Returns

The callback function returns the following parameters:

Parameter NameData TypeDescription
responseJSONObject?The retrieved enrollment data, if the user is enrolled.
errorZSMError?Error details if enrollment retrieval fails.

Enroll: UMFAClient.enroll()

The enroll method initiates the enrollment process for the Universal Multi-Factor Authentication (UMFA). This process binds the user's identity to the device, which is required for subsequent authentication operations.

Usage

client.enroll(userId) { response, error ->
    if (response != null) {
        // Handle successful enrollment, store or use the token
    } else {
        Log.e("UMFAClient", "Error during enrollment: ${error?.localizedMessage}")
    }
}

Parameters

Parameter NameData TypeDescription
userIdStringThe user ID to enroll.
completion(JSONObject?, ZSMError?) -> UnitA callback function that handles the response.

Returns

The callback function returns the following parameters:

Parameter NameData TypeDescription
responseJSONObject?The enrollment response containing the authentication token.
errorZSMError?Error details if enrollment fails.

Authenticate: UMFAClient.authenticate()

The authenticate method performs an authentication operation using the credentials stored on the device. If the credentials are not present, authentication will fail.

Usage

client.authenticate(userId) { response, error ->
    if (response != null) {
        // Handle successful authentication
    } else {
        Log.e("UMFAClient", "Error during authentication: ${error?.localizedMessage}")
    }
}

Parameters

Parameter NameData TypeDescription
userIdStringThe user ID to authenticate.
completion(JSONObject?, ZSMError?) -> UnitA callback function that handles the response.

Returns

The callback function returns the following parameters:

Parameter NameData TypeDescription
responseJSONObject?The authentication response containing the verified token.
errorZSMError?Error details if authentication fails.

Unenroll: UMFAClient.unenroll()

The unenroll method removes a specific user's UMFA credentials from the device.

Usage

client.unenroll(userId) { error ->
    if (error == null) {
        Log.d("UMFAClient", "Successfully unenrolled user")
    } else {
        Log.e("UMFAClient", "Error during unenrollment: ${error.localizedMessage}")
    }
}

Parameters

Parameter NameData TypeDescription
userIdStringThe user ID to unenroll.
completion(ZSMError?) -> UnitA callback function that handles the response.

Returns

The callback function returns the following parameter:

Parameter NameData TypeDescription
errorZSMError?Error details if unenrollment fails; null on success.

Java

Check Enrollment: UMFAClient.checkEnrollment()

The checkEnrollment method checks if a user has previously enrolled in the Universal Multi-Factor Authentication (UMFA) process on the current device. This allows the application to determine whether to proceed with enrollment or authentication flows.

Usage

client.checkEnrollment(userId, (response, error) -> {
    if (response != null) {
        try {
            String rawId = response.getString("rawId");
            // Handle retrieved enrollment data
        } catch (JSONException e) {
            Log.e("UMFAClient", "Error parsing JSON response: " + e.getMessage());
        }
    } else {
        Log.e("UMFAClient", "Error retrieving enrollment data: " + (error != null ? error.getLocalizedMessage() : "Unknown"));
    }
});

Parameters

Parameter NameData TypeDescription
userIdStringThe user ID to check enrollment for.
completionJSONObjectCompletionHandlerA callback interface that handles the response.

The JSONObjectCompletionHandler interface has a single method:

void onComplete(JSONObject result, ZSMError error);

Returns

The callback function returns the following parameters:

Parameter NameData TypeDescription
resultJSONObject?The retrieved enrollment data, if the user is enrolled.
errorZSMError?Error details if enrollment retrieval fails.

Enroll: UMFAClient.enroll()

The enroll method initiates the enrollment process for the Universal Multi-Factor Authentication (UMFA). This process binds the user's identity to the device, which is required for subsequent authentication operations.

Usage

client.enroll(userId, (response, error) -> {
    if (response != null) {
        // Handle successful enrollment, store or use the token
    } else {
        Log.e("UMFAClient", "Error during enrollment: " + (error != null ? error.getLocalizedMessage() : "Unknown"));
    }
});

Parameters

Parameter NameData TypeDescription
userIdStringThe user ID to enroll.
completionJSONObjectCompletionHandlerA callback interface that handles the response.

Returns

The callback function returns the following parameters:

Parameter NameData TypeDescription
resultJSONObject?The enrollment response containing the authentication token.
errorZSMError?Error details if enrollment fails.

Authenticate: UMFAClient.authenticate()

The authenticate method performs an authentication operation using the credentials stored on the device. If the credentials are not present, authentication will fail.

Usage

client.authenticate(userId, (response, error) -> {
    if (response != null) {
        // Handle successful authentication
    } else {
        Log.e("UMFAClient", "Error during authentication: " + (error != null ? error.getLocalizedMessage() : "Unknown"));
    }
});

Parameters

Parameter NameData TypeDescription
userIdStringThe user ID to authenticate.
completionJSONObjectCompletionHandlerA callback interface that handles the response.

Returns

The callback function returns the following parameters:

Parameter NameData TypeDescription
resultJSONObject?The authentication response containing the verified token.
errorZSMError?Error details if authentication fails.

Unenroll: UMFAClient.unenroll()

The unenroll method removes a specific user's UMFA credentials from the device.

Usage

client.unenroll(userId, error -> {
    if (error == null) {
        Log.d("UMFAClient", "Successfully unenrolled user");
    } else {
        Log.e("UMFAClient", "Error during unenrollment: " + error.getLocalizedMessage());
    }
});

Parameters

Parameter NameData TypeDescription
userIdStringThe user ID to unenroll.
completionFunction interface taking a ZSMError?A callback function that handles the response.

Returns

The callback function returns the following parameter:

Parameter NameData TypeDescription
errorZSMError?Error details if unenrollment fails; null on success.

Out-of-Band Token Validation

For out-of-band validation of the token 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"
}