NFC Identity Reading
Read and verify identity documents via NFC chip (passports, national ID cards)
NFC identity reading extracts identity data directly from the chip embedded in passports and national ID cards. The chip stores the same data printed on the document, plus a high-resolution portrait photo, all signed by the issuing country's certificate authority.
Supported documents
| Document | Protocol | Notes |
|---|---|---|
| Passports (ICAO 9303) | BAC (Basic Access Control) | Document number, date of birth, and expiry required for access |
| Spanish DNIe | ISO 14443 | Standard contactless interface |
| Other EU national ID cards | ISO 14443 / PACE | Support varies by issuing country |
How it works
1. Optical data capture
Before reading the NFC chip, the app captures the Machine Readable Zone (MRZ) from the document. The MRZ provides the access keys:
| MRZ field | Used for |
|---|---|
| Document number | BAC key derivation |
| Date of birth | BAC key derivation |
| Date of expiry | BAC key derivation |
For passports, the document number must be padded to 9 characters with < for correct BAC key computation.
2. NFC chip access
The access protocol depends on the document type:
BAC (Basic Access Control): Used by passports. The reader derives session keys from MRZ data to authenticate and establish an encrypted channel.
PACE (Password Authenticated Connection Establishment): Used by newer EU ID cards. Provides stronger key agreement through Diffie-Hellman.
3. Data extraction
Once the secure channel is established, the reader extracts data groups from the chip:
| Data Group | Contents |
|---|---|
| DG1 | MRZ data (name, nationality, dates) |
| DG2 | Biometric portrait photo (JPEG/JPEG2000) |
| DG7 | Signature image (if present) |
| DG11 | Additional personal details (address, phone) |
| DG14 | Security infos for Chip Authentication |
| SOD | Document Security Object (signed hashes of all DGs) |
4. Passive authentication
Passive authentication verifies that the chip data has not been tampered with:
- Extract the Document Security Object (SOD) from the chip.
- Verify the SOD signature against the Document Signer Certificate embedded in it.
- Verify the Document Signer Certificate against the Country Signing Certificate Authority (CSCA) master list.
- Compare the hash of each data group against the hash stored in the SOD.
If all checks pass, you can be certain the data was written by the issuing authority and has not been modified.
CSCA master list
Verifiables serves the CSCA master list used for passive authentication:
GET /nfc-identity/csca
Returns the consolidated CSCA master list (the union of several national master lists) as PEM-encoded certificates (Content-Type: application/x-pem-file). The list contains 580+ certificates from countries worldwide. Responses are cacheable for 1 day (Cache-Control: public, max-age=86400).
Authentication: None (public endpoint)
curl https://api.verifiables.com/nfc-identity/csca \
-o csca_master_list.pemReturns 503 with { "error": "CSCA master list unavailable" } if the list cannot be served.
Client-side read result
The NFC chip is read and verified on the device by the mobile SDK; the reading itself does not go through the API. After a successful read, the SDK produces a result like:
{
"status": "valid",
"document": {
"type": "passport",
"issuingCountry": "FRA",
"documentNumber": "12AB34567",
"expiryDate": "2030-05-15"
},
"identity": {
"givenNames": "Marie Claire",
"familyName": "Dupont",
"dateOfBirth": "1990-01-15",
"nationality": "FRA",
"portrait": "data:image/jpeg;base64,/9j/4AAQ..."
},
"verification": [
{ "status": "OK", "check": "SOD signature valid" },
{ "status": "OK", "check": "Document Signer certificate valid" },
{ "status": "OK", "check": "CSCA chain valid" },
{ "status": "OK", "check": "Data group hashes match" }
]
}This object is produced by the mobile SDK on the device, not returned by any API endpoint.
| Field | Description |
|---|---|
document | Document metadata (type, issuing country, number, expiry) |
identity | Extracted personal data and portrait |
verification | Array of passive authentication checks |
Verifying a read with the API
POST /nfc-identity/verify
Passive authentication of the files your app read from the chip of a passport or ID card, on our servers. Send the raw files as read, base64 encoded; the holder's data is read from the signed data groups and nothing else in the request is trusted. The API checks that every data group matches its hash in the SOD, that the SOD's signed attributes digest its content, that the document signer's signature holds, and that a CSCA of the master list issued the document signer.
Authentication: API key (publishable or secret), or from a mobile app an SDK token minted by your backend (see Native SDK). Under an SDK token minted with requirePresence (the default), a read whose chip presence was not proven answers invalid, with the chip.presence check failed for presence_required.
{
"sessionId": "chip-session-42",
"sod": "dyCCB...",
"dg1": "YVtfH1hQ...",
"dg11": "a0Jc...",
"attempt": { "number": 1 }
}| Field | Type | Required | Description |
|---|---|---|---|
sod | base64 | Yes | EF.SOD as read (with or without its 0x77 tag), 32 kB max |
dg1 | base64 | Yes | EF.DG1 (the MRZ), 1 kB max |
dg2 | base64 | No | EF.DG2 (face image), checked against its hash, 256 kB max |
dg11 | base64 | No | EF.DG11 (full name, place of birth), checked against its hash, 8 kB max |
dg14 | base64 | No | EF.DG14, checked against its hash, 8 kB max |
sessionId | string | No | Your id for the reading session; the attempts of one session are counted as one |
attempt | object | No | { number, reason? }, for your own tracing |
Response 200
{
"sessionId": "chip-session-42",
"verdict": "valid",
"checks": [
{ "id": "sod.readable", "status": "pass" },
{ "id": "dg.hashes", "status": "pass", "detail": "DG1, DG11" },
{ "id": "sod.signature", "status": "pass", "detail": "Document Signer 7" },
{ "id": "dsc.issuer", "status": "pass", "detail": "CSCA Utopia" },
{ "id": "dg1.mrz", "status": "pass", "detail": "TD3" },
{ "id": "dg11.read", "status": "pass" }
],
"claims": {
"documentCode": "P",
"documentType": "passport",
"documentNumber": "L898902C3",
"issuingState": "UTO",
"nationality": "UTO",
"familyName": "ERIKSSON",
"givenNames": "ANNA MARIA",
"birthDate": "1974-08-12",
"expiryDate": "2012-04-15",
"sex": "F",
"placeOfBirth": "ZENITH",
"personalNumber": null,
"mrz": ["P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<", "L898902C36UTO7408122F1204159ZE184226B<<<<<10"],
"nameSource": "dg11"
},
"documentType": "passport",
"issuingState": "UTO",
"expiry": "2012-04-15",
"authorityCountry": "UT"
}verdict is valid only when every check passes and the document signer chains to a CSCA of the master list; claims is null otherwise. A verdict of invalid is still a verdict (the data was checked). Names and the birth date come from DG11 when you send it (full names, four-digit year), from the MRZ otherwise. The portrait is not returned.
Returns 422 with { "error": "sod_unreadable", ... } when the SOD is not an EF.SOD, 503 with { "error": "trust_store_unavailable", ... } when the CSCA master list cannot be loaded (no verdict, retry later), and 422 with { "error": "Validation failed", "issues": [...] } on a malformed body.
Passive authentication proves the data was signed by the issuing State. It does not prove the chip was present when the files were sent.
Submitting a read to the API (legacy)
POST /nfc-identity/read
Legacy. Stores fields your app sends, without any verification; use POST /nfc-identity/verify instead. It does not read the chip and does not return identity data.
Authentication: API key (publishable or secret)
Request body
{
"documentType": "passport",
"firstName": "Marie Claire",
"lastName": "Dupont",
"birthDate": "1990-01-15",
"documentNumber": "12AB34567",
"expiryDate": "2030-05-15",
"nationality": "FRA"
}| Field | Type | Required | Description |
|---|---|---|---|
documentType | "passport" | "cni" | Yes | Document type |
firstName | string | Yes | Given names from the chip |
lastName | string | Yes | Family name from the chip |
birthDate | string | Yes | YYYY-MM-DD |
documentNumber | string | Yes | Document number |
expiryDate | string | Yes | YYYY-MM-DD |
gender | string | No | Gender |
nationality | string | No | Nationality code |
mrz | string | No | Raw MRZ string |
identityNo | string | No | National identity number |
placeOfBirth | string | No | Place of birth |
Response 201
{
"sessionId": "550e8400-e29b-41d4-a716-446655440000",
"expiresIn": 900
}The stored read expires after 900 seconds (15 minutes). Returns 422 with { "error": "Validation failed", "issues": [...] } if the body does not match the schema.
Security considerations
- Chip cloning protection: Active Authentication (AA) or Chip Authentication (CA) can detect cloned chips, but not all documents support it. Passive authentication alone proves data integrity, not chip authenticity.
- MRZ as access key: The MRZ data acts as a shared secret. Physical possession of the document is required to read the chip.
- Portrait matching: The chip portrait is higher quality than the printed photo and can be used for biometric face matching.
- CSCA trust: The CSCA master list is the root of trust. Verifiables builds it from several national master lists and refreshes it daily.