Skip to content

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.

text
Web page  →  CESJsBridge  →  Native CICReader SDK  →  NID chip

CESJsBridge 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 ​

javascript
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); });
}
ParamTypeRequiredDefaultDescription
idCardstringYes—The last 9 digits of the NID number.
dobstringYes—Date of birth, format yyMMdd.
expstringYes—Card expiry date, format yyMMdd.

Using CAN — 12-digit ID number ​

javascript
async function submitForm() {
  const idCard = document.getElementById("idcard").value;
  const result = await CESJsBridge.readNfc(idCard)
    .catch((reason) => { console.log("failed reason: ", reason); });
}
ParamTypeRequiredDefaultDescription
idCardstringYes—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 ​

FieldTypeDescription
comstringUnicode version info and the list of available data groups. Base64.
sodstringThe SOD data from the NFC scan. Base64.
dg1stringIndividual basic details — MRZ information. Base64.
dg2stringCitizen facial image encoding. Base64.
dg13stringCitizen-related information. Base64.
dg14stringUsed for the EAC access mechanism. Base64.
dg15stringPublic key, used for Passive Authentication. Base64.
cloneStatusint0 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 }:

javascript
{ code: 600032, message: "NFC is not supported on this device" }
FieldTypeDescription
NfcNotAvailable600103The device does not support NFC.
NFCNotSupported600032NFC is not supported on this device.
SessionOnGoing600101Another reading session is in progress.
SystemResourceUnavailable600102The NFC system is busy.
UserCancelled3The user cancelled.
Unspecified-1An unexpected error.

The full table is in Error Codes.

Built-in Functions ​

javascript
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 ​

groovy
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")
}
kotlin
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 ​

swift
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 ​

Developers