Aller au contenu

Pipeline biométrique — capture, OCR, MRZ, liveness, face match

Modules : bio-svc (orchestrateur Temporal), mrz-svc, doc-auth-svc, liveness-svc, face-match-svc. Plus ocr-svc (cf ADR-003).

ADRs : ADR-001 (Temporal), ADR-003 (OCR), ADR-027 (fields DOC_KYC/SELFIE), ADR-028 (architecture pipeline).

POC : poc-mrz-parser (Kotlin, ICAO 9303 TD1/TD2/TD3 + 5 checksums).

Standards : ICAO 9303, NIST SP 800-63A IAL2/IAL3, ISO/IEC 30107-3 PAD, iBeta L1/L2, eIDAS LoA Substantial.

Ce document fixe la spec d’ingénierie complète de la pipeline biométrique VitaKYC. Un développeur qui le lit doit pouvoir, sans question résiduelle :

  1. Comprendre le flow E2E d’une vérification d’identité documentaire et biométrique (depuis capture jusqu’à décision).
  2. Implémenter chaque sous-service (bio-svc, mrz-svc, doc-auth-svc, liveness-svc, face-match-svc) avec ses contrats d’API.
  3. Câbler les fallbacks commerciaux (Regula, Onfido, Sumsub, Veriff, Jumio) sans dette d’intégration.
  4. Mesurer la qualité (TAR/FAR face match, PAD APCER/BPCER, AUC document authenticity).
  5. Respecter les obligations conformité (IAL2, BCT 2024, RGPD art. 9).

Flow nominal (~6–10 s p95) :

  1. Client capture document + selfie → upload chiffré
  2. bio-svc lance un workflow Temporal BiometricVerificationWorkflow
  3. Étapes parallèles : OCR + MRZ + doc-auth + liveness + face match
  4. Cross-checks : OCR ↔ MRZ (nom, DOB), doc-auth ↔ MRZ (cohérence pays), liveness ↔ face match (même personne)
  5. Décision agrégée → BiometricVerdict
  6. Évent Kafka bio.verdict.published → consommé par risk-matrix-svc (ajoute dimension AML/biométrique) et case-mgmt-svc (route vers manuel si nécessaire)

TypeAcquisitionVérifications immédiates client
Passeport ICAOrecto (page photo + MRZ) + scan NFC optionneldétection rectangulaire, MRZ visible, focus, glare < 5 %
CNI tunisiennerecto + versodétection rectangulaire 2 faces, hologramme visible côté verso
CIN biométrique TN (depuis 2017)recto + verso, NFC optionnelidem + lecture NFC si supporté
Permis de conduire UErecto + versodétection des couleurs nationales spécifiques
Justificatif domicile (facture)photo simpleOCR + cross-check adresse

API Web SDK (extrait) :

const capture = await vitakyc.captureDocument({
documentType: "passport", // ou "cni_tn", "permis_eu", ...
faces: ["front", "mrz_zone"], // requis selon le type
qualityThresholds: {
blurScore: 0.85, // Laplacian variance normalisé
glareScore: 0.95,
minResolutionDpi: 300,
minFrameCoverage: 0.65 // doc occupe ≥ 65 % du frame
},
retryPolicy: { maxAttempts: 3, hint: "show document fully in frame" }
});
// → { captureId, imageRefs[], qualityReport, deviceFingerprint }
const selfie = await vitakyc.captureSelfie({
liveness: "active", // "passive" | "active" | "hybrid"
challenge: ["blink", "turn_head_left", "smile"],
videoLengthMs: 4000, // pour analyse temporelle PAD
deviceFeatures: ["accelerometer", "gyroscope"] // anti-replay
});
// → { selfieId, videoRef, framesRef[], livenessSignals }

Le SDK Web embarque un détecteur passif local (TensorFlow.js MobileFaceNet quantizé) qui pré-filtre les replays vidéo évidents avant upload — réduit le coût commercial.


@WorkflowInterface
interface BiometricVerificationWorkflow {
@WorkflowMethod
fun verify(input: BiometricInput): BiometricVerdict
}
class BiometricVerificationWorkflowImpl : BiometricVerificationWorkflow {
private val ocr = activityStub<OcrActivities>(timeout = 6.s, retries = 2)
private val mrz = activityStub<MrzActivities>(timeout = 1.s, retries = 1)
private val docAuth = activityStub<DocAuthActivities>(timeout = 8.s, retries = 2)
private val liveness = activityStub<LivenessActivities>(timeout = 12.s, retries = 1)
private val faceMatch = activityStub<FaceMatchActivities>(timeout = 4.s, retries = 2)
override fun verify(input: BiometricInput): BiometricVerdict {
val (ocrResult, mrzResult, docAuthResult) = parallel(
{ ocr.extractFields(input.documentRef) },
{ mrz.parseMrz(input.documentRef) },
{ docAuth.checkAuthenticity(input.documentRef, input.documentType) }
)
val crossCheck = crossCheckOcrMrz(ocrResult, mrzResult)
val (livenessResult, faceMatchResult) = parallel(
{ liveness.detect(input.selfieRef, input.livenessMode) },
{ faceMatch.match(input.documentRef, input.selfieRef) }
)
return aggregate(
input = input,
ocr = ocrResult,
mrz = mrzResult,
crossCheck = crossCheck,
docAuth = docAuthResult,
liveness = livenessResult,
faceMatch = faceMatchResult
)
}
}
Cross-checkRègleEffet sur verdict
OCR.surname ↔ MRZ.surnameLevenshtein distance ≤ 2OK ; sinon flag cross_check_name_mismatch
OCR.dateOfBirth ↔ MRZ.dateOfBirthexact matchOK ; sinon flag cross_check_dob_mismatch
OCR.documentNumber ↔ MRZ.documentNumberexact matchOK ; sinon flag cross_check_doc_no_mismatch
MRZ.country ↔ doc-auth.countryexact matchOK ; sinon flag cross_check_country_mismatch
face-match scorescore_consolidated >= threshold(tenant)OK ; sinon flag face_match_below_threshold
liveness verdictlive (PAD passe)OK ; sinon flag liveness_failed
score_final = (
w_ocr * ocr.confidence +
w_mrz * mrz.checksumScore +
w_doc * docAuth.score +
w_liveness * liveness.score +
w_face * faceMatch.scoreConsolidated
) - sum(flag_penalties)
verdict = score_final >= threshold_pass ? PASS
: score_final >= threshold_review ? MANUAL_REVIEW
: FAIL

Poids et seuils configurables par tenant. Défaut MVP : w_ocr=0.10, w_mrz=0.20, w_doc=0.20, w_liveness=0.25, w_face=0.25. Seuils pass=0.80, review=0.60.


FormatDocument typeLignes × longueurChamps
TD1CNI biométriques (incl. TN)3 lignes × 30doc#, country, surname, given names, DOB, sex, expiry, optional data + 5 check digits
TD2titres de séjour (souvent UE)2 lignes × 36idem TD1 (compact)
TD3passeports2 lignes × 44idem + nationalité distincte du pays émetteur

Pour chaque format :

  1. Découpage des champs par offsets fixes (ICAO 9303-3, -4, -5).
  2. Conversion charset (< filler → space, A-Z + 0-9 + chevrons).
  3. Calcul des 5 chiffres de contrôle ICAO (algorithme 7-3-1) :
    • sur le numéro de document
    • sur la date de naissance
    • sur la date d’expiration
    • sur l’optional data
    • composite final
  4. Si un seul check digit échoue → MRZ rejetée (pas de fallback OCR — la MRZ est déterministe ou absente).
data class MrzResult(
val format: MrzFormat, // TD1, TD2, TD3
val documentNumber: String,
val issuingCountry: IsoAlpha3,
val nationality: IsoAlpha3?, // null pour TD1/TD2
val surname: String,
val givenNames: String,
val dateOfBirth: LocalDate,
val sex: Sex, // M, F, X
val expiryDate: LocalDate,
val optionalData: String?,
val checksumScore: Double, // 1.0 = tous OK ; 0.0 = composite KO
val rawLines: List<String>,
val warnings: List<MrzWarning>
)

POC référence : poc-mrz-parser (~400 LOC Kotlin pur, 18 tests verts dont les vecteurs ICAO publics et des MRZ réels TN/FR).


5. Service doc-auth-svc — authenticité document

Section intitulée « 5. Service doc-auth-svc — authenticité document »

Le service vérifie qu’un document n’est ni falsifié, ni un imprimé, ni un assemblage d’images. Au MVP : adapter Regula Document Reader SDK (binaire on-prem inclus) + heuristiques internes pour les documents tunisiens.

VérificationMéthodeScore
Template matching (typeface, layout)Regula DR SDKtemplateScore ∈ [0,1]
Hologramme / OVI (Optically Variable Ink)analyse multi-angle si capture vidéoholoScore ∈ [0,1]
Patterns de fond (guilloché, microtexte)Regula DR SDKpatternScore ∈ [0,1]
MRZ ↔ visible zone cohérencedéjà cross-check bio-svcflag
Image manipulation (JPEG quantization, copy-move)détecteur PRNU / ELA internetamperScore ∈ [0,1]
RFID NFC (passeport biométrique)si disponible : Active Auth (AA)aaScore ∈ {0, 1}
Modedoc-auth-svc
SaaSRegula Cloud API (recommandé)
On-prem connectéRegula DR SDK on-prem (binaire ~600 MB)
Air-gapheuristiques internes uniquement (templateScore réduit ; règle compensatoire côté Risk Matrix obligatoire)

tamperScore reste interne dans toutes les modes — c’est un détecteur de manipulation d’image entraîné sur un dataset privé MENA. Model card minimale obligatoire (cf §9).


MetricCible MVPCible V2
APCER (Attack Presentation Classification Error Rate) — taux d’attaques classées comme bona fide≤ 5 %≤ 1 %
BPCER (Bona fide Presentation CER) — taux d’utilisateurs honnêtes rejetés≤ 3 %≤ 1,5 %
Latence p95≤ 4 s≤ 2 s
iBeta certificationL2 (via fournisseur)L2 interne
FournisseuriBetaModeLatenceCoût/parcours
Onfido MotionL1 + L2SaaS only2–3 s0,40 USD
Sumsub LivenessL2SaaS + on-prem3–5 s0,30 USD
VeriffL2SaaS only2–4 s0,50 USD
Regula Face SDK LivenessL2SaaS + on-prem1–3 sinclus dans licence DR
Interne MVP (passif)non certifiéon-prem1–2 s0

Tenant sélectionne un fournisseur primaire + un fallback. Routage par feature flag, switchable sans redéploiement.


7. Service face-match-svc — InsightFace ArcFace + fallback

Section intitulée « 7. Service face-match-svc — InsightFace ArcFace + fallback »
  1. Extraction du visage depuis le document (zone photo) via détecteur RetinaFace ONNX.
  2. Extraction du visage depuis le selfie (frame le plus net du video).
  3. Encodage chaque visage en vecteur 512-D via ArcFace ResNet-100 ONNX.
  4. Score = cosine similarity entre les 2 vecteurs ∈ [-1, 1], normalisé en [0, 1].
  5. Seuil pass défaut tenant = 0,72 (TAR ~95 % @ FAR 0,01 % sur LFW + CFP-FP).
scoreConsolidated = w_intern * scoreArcFace + w_commercial * scoreCommercial

Si commercial désactivé : w_intern = 1.0. Si scores divergent (|interne - commercial| > 0,15) → flag face_match_divergent → manual review.

  • Aucun template biométrique transmis externalement (cf DPIA). Les vecteurs ArcFace restent dans face-match-svc, jamais dans Kafka, jamais dans risk-matrix. Seul le score scalaire est propagé.
  • Photo selfie stockée chiffrée KMS (AES-256-GCM, KEK per-tenant), retention 5 ans.
  • Photo document stockée selon obligations BCT (10 ans pièce justificative).

8.1 Client SDK (synchrone polling ou webhook async)

Section intitulée « 8.1 Client SDK (synchrone polling ou webhook async) »
MéthodeEndpointDescription
POST/v1/biometric/captureupload chiffré document + selfie, lance workflow
GET/v1/biometric/verifications/:idpoll status pending / completed / failed
GET/v1/biometric/verifications/:id/verdictrécupère BiometricVerdict (audit complet)
POST/v1/biometric/verifications/:id/manual-decisionagent compliance tranche un cas manual_review_required
POST <tenant_callback_url>
X-VitaKYC-Signature: sha256=...
{
"schema": "biometric.verdict.v1",
"verification_id": "ver_abc123",
"tenant_id": "TN-BANQUEX",
"verdict": "PASS",
"score_consolidated": 0.92,
"subscores": {
"ocr": 0.94,
"mrz": 1.00,
"doc_auth": 0.88,
"liveness": 0.96,
"face_match": 0.91
},
"flags": [],
"completed_at": "2026-04-26T10:42:13Z"
}

Spec complète — voir /api/openapi/ section Biometric. Contract tests Pact en CI.


Chaque modèle ONNX déployé est accompagné d’un model card YAML inline conservé dans le repo bio-models/ :

modelId: face_match_arcface_resnet100
version: 1.2.0
hash: sha256:1d4f...
trainingDataset:
source: "MS-Celeb-1M (cleaned by InsightFace) + private TN/MENA augmentation"
size: 5_800_000_images
ethnicityBalance:
- { region: NorthAfrica, share: 0.18 }
- { region: MiddleEast, share: 0.15 }
- { region: Europe, share: 0.32 }
- { region: Asia, share: 0.22 }
- { region: Other, share: 0.13 }
metrics:
TAR_at_FAR_0_001: 0.964
TAR_at_FAR_0_0001: 0.951
benchmark: ["LFW", "CFP-FP", "AgeDB-30", "Private-MENA-test-2026Q1"]
intendedUse: KYC face match — document photo vs live selfie
limitations:
- "Performance dégradée sur visages partiellement occlus (>30%)"
- "Performance dégradée si âge document - âge selfie > 8 ans"
fairnessAssessment:
byEthnicity:
NorthAfrica: { TAR_FAR_0_001: 0.962 }
MiddleEast: { TAR_FAR_0_001: 0.958 }
Europe: { TAR_FAR_0_001: 0.971 }
Asia: { TAR_FAR_0_001: 0.949 }
byGender:
M: { TAR_FAR_0_001: 0.965 }
F: { TAR_FAR_0_001: 0.961 }
approvedBy: ["compliance@vitakyc.io", "security@vitakyc.io"]
deployedAt: 2026-04-25
nextReviewAt: 2026-10-25

RéférenceExigenceCouverture
NIST SP 800-63A IAL2Identity proofing avec doc gov + biométrieOCR + MRZ + face match + liveness ✓
NIST SP 800-63A IAL3Idem + supervision (in-person ou supervised remote)session live + agent compliance video V2
eIDAS LoA Substantialéquivalent IAL2
eIDAS LoA Highdoc à puce + face match certifiéNFC + Regula DA + face match certifié V2
BCT Circulaire 2024onboarding distant banque exige liveness actifiBeta L2 via fournisseur ✓
RGPD art. 9données biométriques = catégorie spécialebase légale consentement explicite + DPIA + retention limitée ✓
ISO/IEC 30107-3PAD methodologyAPCER/BPCER mesurés, certifié L2 fournisseur ✓
ICAO 9303MRZ format et checksumsparser interne POC ✓

MetricCible MVPCible V2
Latence p95 pipeline complète≤ 8 s≤ 5 s
Throughput / pod bio-svc25 verif/min80 verif/min
Coût marginal médian / parcours≤ 0,80 USD≤ 0,30 USD
Disponibilité bio-svc99,5 %99,9 %
Taux fallback face match commercial≤ 25 %≤ 10 %

  • Chiffrement : photos document + selfie chiffrées AES-256-GCM avec DEK per-verification, KEK per-tenant via KMS Vault. Clés rotées tous les 12 mois (cf ADR-002 multi-tenant).
  • Transit : mTLS entre bio-svc et sous-services + adapters commerciaux. Cert pinning côté SDK Web.
  • Anti-replay : signature HMAC du payload de capture avec timestamp + sessionId ; rejet si timestamp > 5 min.
  • Rate limit : 30 verif/min/tenant, 5/min/utilisateur (prévient brute force d’un agent compromis).
  • Audit trail : chaque étape pipeline tracée append-only (event sourcing) avec traceId Temporal. Conservé 10 ans.
  • Anti-bot : reCAPTCHA Enterprise au début du parcours web. Sur mobile, Play Integrity + DeviceCheck.

ItemMVP (V0)V2 (S+12)
MRZ parser interne✓ TD1/TD2/TD3+ TD3 émetteurs rares (CN, SA, autres)
OCR interneTesseract + ONNX MENAOCR transformer custom (better arabic)
doc-authRegula commercial (recommandé)+ heuristiques internes étendues
Livenesscommercial iBeta L2tentative internalisation iBeta L2
Face matchInsightFace ArcFace ONNX+ Vision Transformer fine-tuné MENA
NFC passport readingoptionnelactivé pour eIDAS LoA High
PAD niveau 3nonoui (résistance masques 3D)
Deepfake detectiondétecteur basiquedétecteur 2026-grade (modèle dédié)

  • mrz-svc : POC livré, déployé, contrat OpenAPI publié, 18 tests passants
  • face-match-svc : modèle ArcFace ONNX déployé, model card validée, métriques mesurées sur Private-MENA-test
  • liveness-svc : adapter Onfido + Sumsub configurés, switch tenant fonctionnel
  • doc-auth-svc : Regula DR SDK on-prem installé chez tenant pilote
  • bio-svc : workflow Temporal BiometricVerificationWorkflow en prod, 0 timeout sur 100 parcours pilote
  • Cross-checks OCR ↔ MRZ ↔ doc-auth opérationnels
  • Audit trail append-only conservé 10 ans
  • Photos chiffrées KMS, retention configurée 5 ans / 10 ans
  • Aucun template biométrique sortant hors face-match-svc
  • Webhook signé HMAC, retries, signature vérifiée côté tenant
  • Métriques iBeta L2 documentées par fournisseur
  • DPIA mise à jour pour traitement biométrique (cf DPIA)
  • Runbook on-call : flow incident liveness commercial down, fallback dégradé


Document de spec pipeline biométrique — version 1.0 (2026-04-26). Mises à jour bloquantes nécessitent un ADR.