Custom Challenge

šŸ†” Custom Challenge – Overview

A Custom Challenge allows the REL-ID server to initiate a custom-defined, app-specific step during activation or credential flows. This is useful for cases such as:

  • User consent workflows
  • External KYC integrations
  • Location/GPS validation
  • Biometric vendor-specific checks

This challenge is delivered via the onHandleCustomChallenge event and must be rendered and handled by the host application.


šŸ” Security & Use Cases

  • Fully handled on the client side, enabling deep integration with third-party flows
  • REL-ID server acts as a coordinator, expecting the client to fulfill a known contract
  • Often used in regulated environments (e.g., Aadhaar consent, custom biometrics)

šŸ”„ How It Works

  1. SDK triggers onHandleCustomChallenge
  2. App receives a structured payload with UI instructions
  3. App displays input field and description
  4. User submits their data eg: National ID
  5. App sends the input using setCustomChallengeResponse()

onHandleCustomChallenge Event

Register an event listener for the event, use event name as onHandleCustomChallenge and payload as described below

šŸ“„ Sample Payload

{
  "userID": "testuser",
  "challenge": "{ 
    \"chlng_name\": \"NationalID\", 
    \"chlng_idx\": 4,
    \"chlng_info\": [
      { \"key\": \"Response Label\", \"value\": \"NationalID\" },
      { \"key\": \"description\", \"value\": \"Enter your National ID\" }
    ],
    \"attempts_left\": 3,
    \"max_attempts_count\": 0,
    \"chlng_resp\": [
      { \"challenge\": \"NationalID\", \"response\": \"\" }
    ],
    \"chlng_type\": 1,
    \"chlng_response_validation\": false
  }",
  "status": {
    "statusCode": 100,
    "statusMessage": "Success"
  },
  "error": {
    "longErrorCode": 0,
    "shortErrorCode": 0,
    "errorString": "Success"
  }
}

🧾 Key Fields Explained

FieldTypeDescription
userIDStringUser identifier for whom the challenge is issued
challengeString (JSON)Embedded JSON string containing all challenge metadata
chlng_nameStringName of the custom challenge (e.g., "NationalID")
chlng_idxIntegerIndex or order of the challenge
chlng_infoArrayAdditional info (e.g., description, labels) to be shown in the UI
attempts_leftIntegerNumber of remaining attempts
max_attempts_countIntegerMaximum allowed attempts (0 means unlimited or unspecified)
chlng_respArrayPlaceholder for submitting the user's response
chlng_typeIntegerType of challenge (used for routing logic in SDK or backend)
chlng_promptArrayUI prompts or labels if applicable
chlng_response_validationBooleanIndicates if SDK should auto-validate the response
challenge_response_policyArrayOptional policy definitions for validating the response
chlng_cipher_specArrayCipher spec, if response encryption is needed
sub_chlng_countIntegerNumber of sub-challenges inside this challenge
chlngs_per_batchIntegerUsed when challenges are batched together
status, errorObjectsStatus codes and messages for handling response state

āœ… Submitting the Response

šŸ“¤ setCustomChallengeResponse API

The setCustomChallengeResponse method is used to submit a response to a custom challenge delivered via onHandleCustomChallenge, such as a National ID prompt.


šŸ”§ Basic Flow

  1. Parse the challenge field (JSON string) from the event.
  2. Populate the response field inside chlng_resp.
  3. Convert the updated object back to a JSON string.
  4. Submit it via setCustomChallengeResponse().

šŸ“± Platform-specific Code Samples

Follow below steps to add the user entered value in the challenge JSON:

  1. Parse the challenge JSON and retrieve the ā€œchlng_respā€ JsonArray.
  2. From that JsonArray, retrieve the first JsonObject and set the value of the ā€œresponseā€ key withthe value the user has entered.
šŸ’™ React Native
// Register event listener
let onHandleCustomChallengeSubscription = rdnaEventRegistery.addListener(
  'onHandleCustomChallenge',
  this.onHandleCustomChallenge.bind(this)
);

// Handler method
onHandleCustomChallenge(event) {
  let parsedChallenge = JSON.parse(event.challenge);
  parsedChallenge.chlng_resp[0].response = "123456789"; // User input

  RdnaClient.setCustomChallengeResponse(
    JSON.stringify(parsedChallenge),
    (response) => {
      console.log("Custom challenge submitted successfully:", response);
    }
  );
}

🟣 Flutter
// Register event listener
rdnaClient.on(
  RdnaClient.onHandleCustomChallenge,
  onHandleCustomChallenge
);

// Handler method
void onHandleCustomChallenge(RDNAHandleCustomChallenge event) {
  Map<String, dynamic> challenge = jsonDecode(event.challenge);
  challenge["chlng_resp"][0]["response"] = "123456789";

  rdnaClient.setCustomChallengeResponse(jsonEncode(challenge));
}

🧩 Cordova
// Register event listener
document.addEventListener(
  'onHandleCustomChallenge',
  this.onHandleCustomChallenge.bind(this),
  false
);

// Handler method
onHandleCustomChallenge(event) {
  let parsedChallenge = JSON.parse(event.challenge);
  parsedChallenge.chlng_resp[0].response = "123456789";

  com.uniken.rdnaplugin.RdnaClient.setCustomChallengeResponse(
    () => console.log("Success"),
    (err) => console.error("Error", err),
    [JSON.stringify(parsedChallenge)]
  );
}

šŸ iOS (Objective-C)
// Callback method
(void)onHandleCustomChallenge:(NSString *)userID
                    challenge:(NSString*)challengeJson
                      status:(RDNARequestStatus *)status
                       error:(RDNAError *)error {

  NSError *error;
  NSData *data = [challengeJson dataUsingEncoding:NSUTF8StringEncoding];
  NSMutableDictionary *challengeDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];

  challengeDict[@"chlng_resp"][0][@"response"] = @"123456789";

  NSData *updatedData = [NSJSONSerialization dataWithJSONObject:challengeDict options:0 error:nil];
  NSString *updatedString = [[NSString alloc] initWithData:updatedData encoding:NSUTF8StringEncoding];

  [rdnaInstance setCustomChallengeResponse:updatedString];
}

šŸ¤– Android (Java)
// Callback method
void onHandleCustomChallenge(String userID,
                             String challengeJSON,
                             RDNARequestStatus requestStatus,
                             RDNAError error) {

  JSONObject challenge = new JSONObject(challengeJSON);
  JSONArray chlngResp = challenge.getJSONArray("chlng_resp");
  chlngResp.getJSONObject(0).put("response", "123456789");

  rdna.setCustomChallengeResponse(challenge.toString());
}

🧠 Tips

  • Make sure to sanitize and validate user input before inserting it.
  • Always use the original challenge object and only modify the chlng_resp[].response field.
  • Handle the response or errors appropriately in the callback.

āŒ Error Handling

Status CodeMeaningApp Action
153Attempts exhaustedTerminate challenge, follow fallback
166User is lockedShow a pop-up dialog with the error message and prevent further login attempts until the cooling period ends

🧠 Notes

  • This challenge is dynamic — your app must parse and render UI based on chlng_info.
  • Validate and sanitize user input (e.g., numeric-only, length).
  • Use attempts_left to control retry logic.