Onfido LogoOnfido Logo

Developers

NFC for Document report

Start here

Recent passports, national identity cards and residence permits contain a chip that can be accessed using Near Field Communication (NFC). The Onfido SDKs provide a set of screens and functionalities to extract this information, verify its authenticity and provide the resulting verification as part of a Document report.

NFC is available via Onfido's iOS and Android SDKs.

Follow this guide to:

  • Configure NFC with Onfido Studio
  • Use NFC directly with the Onfido SDKs
  • Interpret the verification results in a Document report

You can view the list of currently supported documents for NFC.

Please contact your Customer Success manager for additional help with integrating NFC.

Using NFC

NFC in Onfido Studio

It is recommended to enable and manage NFC in Onfido Studio.

In Studio, NFC extraction is controlled by a single toggle within the Document Capture task:

With NFC enabled:

  • Studio will instruct the Onfido SDKs to prompt users to scan their document via NFC (if both the document and the device are NFC capable)
  • Users with NFC-enabled documents will be asked to scan their document and can only skip NFC scanning after 5 failed attempts
  • If a user selects a document without NFC, they will not be asked to scan NFC
  • Document report tasks linked to this task will automatically generate the issuing_authority breakdown detailing the NFC verification steps
  • Any subsequent Facial Similarity tasks will use the applicant's picture present in the NFC chip by default for comparison

Configuring NFC in the Onfido SDKs

NFC extraction is enabled by default since the release of the following Onfido SDK versions:

SDKVersionTechnical Documentation
iOS29.1.0ios SDK reference
Android18.1.0Android SDK reference
React Native9.0.0React Native SDK reference
Flutter4.0.0Flutter SDK reference

Please refer to the relevant instructions for the detailed steps on how to declare the necessary dependencies and permissions in your application.

To activate NFC in older SDK versions, please refer to the corresponding SDK version's technical reference.

NFC results in the Document Report

Upgrade to API v3.2+

You must upgrade your backend to use API v3.2 or later in order to receive the issuing_authority breakdown and sub-breakdowns that contain the details of the NFC verification in the Document report.

NFC is not available in earlier versions of the Onfido API. Please refer to our API versioning policy.

Create a check containing a Document report with NFC

Once you have enabled NFC in the Onfido SDK and upgraded to API v3.2 or later, you can create a check containing a Document report with NFC.

Start the SDK flow

The SDK flow for a Document report with NFC includes both the standard document step capture screens and additional NFC scan screens.

All users will first be presented with the document capture screens. The additional NFC screens will then be presented to the user if both the user's device and the document type support NFC. Otherwise, the user will finish the SDK flow after the document capture step and no NFC scan will be completed.

Document IDs

On success the SDK will return a callback which contains an array of document IDs. The document IDs will include:

  • the document front image
  • the document back image (if the document has 2 sides, for example an ID card)
  • the NFC media (if the document is supported for NFC and an NFC scan has completed successfully)

You need to extract all the document IDs to use when creating a check on the backend.

iOS

swift
1func getDocumentIds(fromResults results: [OnfidoResult]) -> [String]? {
2 return results.map({ onfidoResult -> [String] in
3 switch onfidoResult {
4 case .document(let documentResult):
5 guard let nfcMediaId = documentResult.nfcMediaId else {
6 return []
7 }
8 var ids: [String] = [documentResult.front.id]
9 if let backId = documentResult.back?.id {
10 ids.append(backId)
11 }
12 ids.append(nfcMediaId)
13 return ids
14 default:
15 return []
16 }
17 }).first
18 }
19
20OnfidoFlow(...)
21 .with(responseHandler: { response in
22 switch response {
23 case .success(let results):
24 if let documentIds = getDocumentIds(fromResults: results) {
25 // USE DOCUMENT IDS FOR CHECK CREATION
26 }
27 ...
28 }
29 })
objc
1(NSArray<NSString*>*)getDocumentIds:(NSArray *)flowResults {
2 ONFlowResult *flowResult = [flowResults firstObject];
3 if (flowResult.type == ONFlowResultTypeDocument) {
4 ONDocumentResult *documentResult = (ONDocumentResult *)flowResult.result;
5 NSMutableArray *documentIds = [NSMutableArray new];
6 [documentIds addObject:documentResult.front.id];
7 if (documentResult.nfcMediaId != nil) {
8 [documentIds addObject:documentResult.nfcMediaId];
9 }
10 if (documentResult.back != nil) {
11 [documentIds addObject:documentResult.back.id];
12 }
13 return [NSArray arrayWithArray:documentIds];
14 }
15 return @[];
16}
17
18ONFlow *flow = [[ONFlow alloc] initWithFlowConfiguration:config];
19[flow withResponseHandler:^(ONFlowResponse *response) {
20 if (flowResponse.results) {
21 NSArray *documentIds = [self getDocumentIds:flowResponse.results];
22 // Use document IDs for check creation
23 } else if (flowResponse.error) {
24 // Handle Error
25 } else if (flowResponse.userCanceled) {
26 // Handle User Canceled Action
27 }
28}, dismissFlowOnCompletion: /* Dismiss Completion Here */ ];

Android

kotlin
1override fun userCompleted(captures: Captures) {
2 val documentIds = captures.toJson()
3 // add the document ids to check creation request body
4 add("document_ids", documentIds)
5}
6
7private fun Captures.toJson(): JsonArray {
8 val array = JsonArray()
9 document?.nfcMediaUUID?.let { uuid -> array.add(uuid) }
10 document?.front?.let { frontSide -> array.add(frontSide.id) }
11 document?.back?.let { backSide -> array.add(backSide.id) }
12 return array
13}
java
1oid userCompleted(@NonNull Captures captures) {
2 JsonArray jsonCaptures = capturesToJson(captures);
3
4 // add the document ids to check creation request body
5 JsonObject requestBody = new JsonObject();
6 requestBody.add("document_ids", jsonCaptures);
7}
8
9JsonArray capturesToJson(@NonNull Captures captures) {
10 JsonArray array = new JsonArray();
11 if (captures != null) {
12 Document document = captures.getDocument();
13 if (document != null) {
14 String nfcMediaUUID = document.getNfcMediaUUID();
15 if (nfcMediaUUID != null) {
16 array.add(nfcMediaUUID);
17 }
18 DocumentSide frontSide = document.getFront();
19 if (frontSide != null) {
20 array.add(frontSide.getId());
21 }
22 DocumentSide backSide = document.getFront();
23 if (backSide != null) {
24 array.add(backSide.getId());
25 }
26 }
27 }
28 return array;
29}

React Native

js
1Onfido.start({
2 sdkToken: 'sdkTokenFromOnfidoServer',
3 flowSteps: {
4 welcome: true,
5 captureFace: {
6 type: OnfidoCaptureType.VIDEO,
7 },
8 captureDocument: {
9 docType: OnfidoDocumentType.DRIVING_LICENCE,
10 countryCode: OnfidoCountryCode.USA,
11 }
12 },
13 })
14 .then(res =>
15 const result = JSON.stringify(res);
16 const nfcMediaId = result.document.nfcMediaId ?? result.document.nfcMediaUUID;
17 const frontId = result.document.front.id;
18 const backId = result.document.back.id;
19
20 )
21 .catch(err => console.warn('OnfidoSDK: Error:', err.code, err.message));

Flutter

kotlin
1private var result: MethodChannel.Result? = null
2
3override fun userCompleted(captures: Captures) {
4 // deserialize the received captures as per your needs, for example:
5 result?.success(captures.toFlutterResult())
6 result = null
7}
8
9internal fun Captures.toFlutterResult(): Any {
10 val elements = mutableMapOf<String, Any>()
11 this.document?.let {
12 elements["document"] = it.deserialize()
13 }
14 this.face?.let {
15 elements["face"] = it.deserialize()
16 }
17 return listOf(elements)
18}
19
20private fun Face.deserialize(): Map<*, *> {
21 return mapOf("id" to id, "variant" to this.variant.ordinal)
22}
23
24private fun Document.deserialize(): Map<*, *> {
25 val map = mutableMapOf<String, Any>()
26 map["typeSelected"] = this.type.toString().lowercase()
27 map["nfcMediaId"] = this.nfcMediaUUID.toString()
28 front?.let {
29 map["front"] = mapOf("id" to it.id)
30 }
31 back?.let {
32 map["back"] = mapOf("id" to it.id)
33 }
34 return map
35}

Create a check

On success, create a check on your backend.

Example for an NFC supported passport:

bash
1curl -X POST https://api.eu.onfido.com/v3.2/checks \
2 -H 'Authorization: Token token=<YOUR_API_TOKEN>' \
3 -H 'Content-Type: application/json' \
4 -d '{
5 "applicant_id": "<APPLICANT_ID>",
6 "report_names": ["document"],
7 "document_ids": ["<DOCUMENT_ID_FRONT>", "<NFC_MEDIA_ID>"]
8}'
ParameterNotes
applicant_id (required)Specifies the applicant for the check.
report_names (required)The report name. NFC is available as part of the primary Document report option.
document_ids (required)Array including all the document IDs returned in the SDK success callback. This could include up to 3 IDs for the document front side, back side and NFC media.

Note: document_ids is only a required parameter for a Document report with NFC as you need to specify the NFC media ID at check creation. For a standard Document report, document_ids is an optional parameter.

For more details on how to create a check, see our quick start guide.

Result handling

Report breakdown

The issuing_authority breakdown captures the result of the NFC scan. It asserts whether data on the document matches the issuing authority data and uses the following sub-breakdowns:

  • nfc_passive_authentication - asserts the data integrity of the NFC data
  • nfc_active_authentication - asserts whether the document NFC chip is original or cloned

Result logic

Passive Authentication and Active Authentication are checked whenever supported by the underlying document. Each check can return a result of clear, consider or null as below:

nfc_passive_authenticationnfc_active_authentication
Not supported by documentnullnull
Unable to read datanullnull
Failedconsiderconsider
Succeededclearclear

The individual results are then combined in the issuing_authority breakdown into a single result:

nfc_active_authentication is nullnfc_active_authentication is considernfc_active_authentication is clear
nfc_passive_authentication is nullnullconsiderclear
nfc_passive_authentication is considerconsiderconsiderconsider
nfc_passive_authentication is clearclearconsiderclear

The overall document verification result is influenced by the issuing_authority breakdown as follows. Note that if fallback to Visual checks is disabled, the Issuing Authority section would be the main driver to the overall result.

Issuing Authority BreakdownOverall Document Verification
nullrejected
considersuspected
clearclear

Note that if the document's NFC data contains the applicant's photograph, this photograph will be used in any subsequent facial similarity checks, if configured.

Configuration options

In addition to the NFC verification described in this document, you can configure your account to also trigger Visual Document Verification whatever the issuing_authority breakdown result (clear, consider or null). In those cases, the visual authentication, image integrity and data consistency checks will be performed and the results returned in the respective report breakdowns.

Please contact your Customer Success manager to enable this.

Properties

In the Document check properties an nfc object is returned which includes the NFC extracted document data.

If NFC is not available, no data will be extracted and the nfc object will not be returned.

Addendum

Disabling NFC and Removing SDK dependencies

As NFC is enabled by default and library dependencies are included in the build automatically, the following section details the steps required to disable NFC and remove any libraries from the build process.

iOS

  • Disable Near Field Communication Tag Reading capability in your app target. You can follow the steps in Apple's documentation
  • Remove any entries relating to the NFC setup from your app target's Info.plist file (please refer to the iOS SDK documentation)
  • Pass the following method to disable NFC in the iOS SDK.
swift
1let config = try! OnfidoConfig.builder()
2 .withSDKToken("<YOUR_SDK_TOKEN_HERE>")
3 .withDocumentStep()
4 .disableNFC()
5 .build()
objc
1ONFlowConfigBuilder *configBuilder = [ONFlowConfig builder];
2
3[configBuilder withSdkToken:@"YOUR_SDK_TOKEN_HERE"];
4[configBuilder withDocumentStep];
5[configBuilder disableNFC];
6
7NSError *configError = NULL;
8ONFlowConfig *config = [configBuilder buildAndReturnError:&configError];

Android

  • Remove any dependencies (with the specified versions) relating to NFC from your build script. Please refer to the Android SDK documentation
  • Pass the following method to remove the NFC document extraction step from the Android SDK flow.
kotlin
1val config = OnfidoConfig.builder(this@MainActivity)
2 .withSDKToken(<YOUR_SDK_TOKEN_HERE>)
3 .disableNFC()
4 .withCustomFlow(flowSteps)
5 .build()
java
1OnfidoConfig config = OnfidoConfig.builder(context)
2 .withSDKToken(<YOUR_SDK_TOKEN_HERE>)
3 .disableNFC()
4 .withCustomFlow(flowSteps)
5 .build()

React Native

  • Remove any dependencies relating to NFC. Please refer to the relevant section of the React Native SDK Reference
  • Include the following initialisation option to remove the NFC document extraction step from the React Native SDK flow.
js
1config = {
2 sdkToken:<YOUR_SDK_TOKEN_HERE>,
3 flowSteps: {
4 <YOUR_FLOW_STEPS_CONFIGURATION_HERE>
5 },
6 disableNFC: true
7}

Flutter

  • Remove any dependencies relating to NFC. Please refer to the relevant section of the Flutter Reference
  • Include the following initialisation option to remove the NFC document extraction step from the Flutter SDK flow.
js
1config = {
2 sdkToken:<YOUR_SDK_TOKEN_HERE>,
3 flowSteps: {
4 <YOUR_FLOW_STEPS_CONFIGURATION_HERE>
5 },
6 disableNFC: true
7}

Table of contents
NFC for Document report
NFC results in the Document Report
Addendum