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
- SDK triggers
onHandleCustomChallenge
- App receives a structured payload with UI instructions
- App displays input field and description
- User submits their data eg: National ID
- App sends the input using
setCustomChallengeResponse()
onHandleCustomChallenge
Event
onHandleCustomChallenge
EventRegister 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
Field | Type | Description |
---|---|---|
userID | String | User identifier for whom the challenge is issued |
challenge | String (JSON) | Embedded JSON string containing all challenge metadata |
chlng_name | String | Name of the custom challenge (e.g., "NationalID") |
chlng_idx | Integer | Index or order of the challenge |
chlng_info | Array | Additional info (e.g., description, labels) to be shown in the UI |
attempts_left | Integer | Number of remaining attempts |
max_attempts_count | Integer | Maximum allowed attempts (0 means unlimited or unspecified) |
chlng_resp | Array | Placeholder for submitting the user's response |
chlng_type | Integer | Type of challenge (used for routing logic in SDK or backend) |
chlng_prompt | Array | UI prompts or labels if applicable |
chlng_response_validation | Boolean | Indicates if SDK should auto-validate the response |
challenge_response_policy | Array | Optional policy definitions for validating the response |
chlng_cipher_spec | Array | Cipher spec, if response encryption is needed |
sub_chlng_count | Integer | Number of sub-challenges inside this challenge |
chlngs_per_batch | Integer | Used when challenges are batched together |
status , error | Objects | Status codes and messages for handling response state |
✅ Submitting the Response
📤 setCustomChallengeResponse
API
setCustomChallengeResponse
APIThe setCustomChallengeResponse
method is used to submit a response to a custom challenge delivered via onHandleCustomChallenge
, such as a National ID prompt.
🔧 Basic Flow
- Parse the
challenge
field (JSON string) from the event. - Populate the
response
field insidechlng_resp
. - Convert the updated object back to a JSON string.
- Submit it via
setCustomChallengeResponse()
.
📱 Platform-specific Code Samples
Follow below steps to add the user entered value in the challenge JSON:
- Parse the challenge JSON and retrieve the “chlng_resp” JsonArray.
- 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 thechlng_resp[].response
field. - Handle the response or errors appropriately in the callback.
❌ Error Handling
Status Code | Meaning | App Action |
---|---|---|
153 | Attempts exhausted | Terminate challenge, follow fallback |
166 | User is locked | Show 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.
Updated 2 months ago