JavaScript Bridge
Read the NID chip from a web page running inside an Android or iOS WebView. The native SDK injects a CESJsBridge object into the page; your JavaScript calls it and the native layer does the NFC work. Version 1.0.8.
How It Works
The web page cannot touch NFC hardware directly. The host app embeds the CICReader native SDK, registers a JavaScript interface on its WebView, and the page calls through that bridge.
Web page → CESJsBridge → Native CICReader SDK → NID chipCESJsBridge is injected when the page first loads, so wait for the page-loaded callback before calling it.
INFO
The bridge returns only the raw chip data — com, sod, dg1, dg2, dg13, dg14, dg15 and cloneStatus. It does not return the parsed citizen fields or a verificationKey. Send the data groups to Read NFC from your backend to get those.
Read the Chip
Using MRZ — 9-digit ID, date of birth, expiry date
async function submitForm() {
const idCard = document.getElementById("idcard").value;
const dob = document.getElementById("dob").value;
const exp = document.getElementById("exp").value;
const result = await CESJsBridge.readNfc(idCard, dob, exp)
.catch((reason) => { console.log("failed reason: ", reason); });
}| Param | Type | Required | Default | Description |
|---|---|---|---|---|
idCard | string | Yes | — | The last 9 digits of the NID number. |
dob | string | Yes | — | Date of birth, format yyMMdd. |
exp | string | Yes | — | Card expiry date, format yyMMdd. |
Using CAN — 12-digit ID number
async function submitForm() {
const idCard = document.getElementById("idcard").value;
const result = await CESJsBridge.readNfc(idCard)
.catch((reason) => { console.log("failed reason: ", reason); });
}| Param | Type | Required | Default | Description |
|---|---|---|---|---|
idCard | string | Yes | — | The full 12-digit NID number. |
The same method name takes either one or three arguments — the bridge picks the scheme from the argument count.
Result
| Field | Type | Description |
|---|---|---|
com | string | Unicode version info and the list of available data groups. Base64. |
sod | string | The SOD data from the NFC scan. Base64. |
dg1 | string | Individual basic details — MRZ information. Base64. |
dg2 | string | Citizen facial image encoding. Base64. |
dg13 | string | Citizen-related information. Base64. |
dg14 | string | Used for the EAC access mechanism. Base64. |
dg15 | string | Public key, used for Passive Authentication. Base64. |
cloneStatus | int | 0 not checked · 1 good · 2 alert · 8 error. |
Note cloneStatus arrives as an integer here, not the enum the native SDKs return.
Errors
Rejections carry { code, message }:
{ code: 600032, message: "NFC is not supported on this device" }| Field | Type | Description |
|---|---|---|
NfcNotAvailable | 600103 | The device does not support NFC. |
NFCNotSupported | 600032 | NFC is not supported on this device. |
SessionOnGoing | 600101 | Another reading session is in progress. |
SystemResourceUnavailable | 600102 | The NFC system is busy. |
UserCancelled | 3 | The user cancelled. |
Unspecified | -1 | An unexpected error. |
The full table is in Error Codes.
Built-in Functions
async function checkNfcAvailability() {
const result = await CESJsBridge.isNfcAvailable();
}
async function checkNfcEnable() { // Android only
const result = await CESJsBridge.isNfcEnable();
}
async function openNfcSetting() { // Android only
await CESJsBridge.openNfcSetting();
}Host App Setup
WARNING
The bridge hands raw chip data to whatever page is loaded in the WebView. Load only your own HTTPS origins, block navigation to any other origin, and check the page origin in the native interface before running a read.
Android
allprojects {
repositories {
maven { url "https://maven.google.com" }
maven { url "https://jcenter.bintray.com" }
maven { url "path/to/cic_reader" }
}
}
dependencies {
implementation("vn.cake:cic-reader:1.x.x")
}val jsInterface = CICJsInterface(activity, object : JavascriptEvaluator {
override fun execute(jsFunc: String) {
webView.evaluateJavascript(jsFunc) {}
}
})
webView.addJavascriptInterface(jsInterface, CICJsInterface.JS_KEY)
webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
jsInterface.pageLoaded() // call this when the page finishes loading
}
}INFO
The Android artefact for the bridge is vn.cake:cic-reader, while the standalone Android SDK uses vn.cake.ekyc.sdk:eid. Check which coordinate your build expects.
iOS
let jsInterface = CESNfcJsInterface()
wk.configuration.userContentController.addJavascriptInterface(jsInterface, forKey: CESNfcJsInterface.kJsKey)
jsInterface.delegate = self
// WKWebView — when the page finishes loading
func webView(_: WKWebView, didFinish _: WKNavigation!) {
jsInterface.pageLoaded()
}
@objc func onJavascriptEvaluate(script: String) {
webView.evaluateJavaScript(script)
}iOS also needs the NFC capability, the NFCReaderUsageDescription key and the three ISO7816 identifiers — see iOS SDK Integration.
Flutter
Use the Flutter plugin directly rather than the bridge.
Next Steps
- Read NFC — submit the data groups the bridge returns
- Error Codes — the full error table
- Mobile SDK Overview — concepts shared across platforms