@41devs/naya-id-js 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,5 @@
1
+ © 2026 41devs. Tous droits réservés.
2
+
3
+ Ce logiciel est distribué pour l'intégration des services Naya ID.
4
+ Toute reproduction ou utilisation en dehors de ce cadre nécessite
5
+ l'accord écrit de 41devs.
package/README.md ADDED
@@ -0,0 +1,272 @@
1
+ # Naya ID — SDK JavaScript
2
+
3
+ SDK JavaScript officiel de **Naya ID**, la vérification d'identité (eKYC) clé en
4
+ main pour l'Afrique de l'Ouest (zone CEDEAO). **Aucun framework, aucune étape de
5
+ build côté client** : vous ajoutez une balise `<script>`, vous appelez **une
6
+ seule méthode**, l'interface Naya s'ouvre en plein écran, prend en charge la
7
+ capture du document, la détection de vivacité et la décision, puis vous rend le
8
+ résultat et rend la main à votre page.
9
+
10
+ C'est le pendant « vanilla » des SDK [Flutter](https://pub.dev/packages/naya_id)
11
+ et [Angular](https://www.npmjs.com/package/@41devs/naya-id-angular) : même
12
+ parcours, mêmes écrans, même contrat d'API.
13
+
14
+ - **npm** : [`@41devs/naya-id-js`](https://www.npmjs.com/package/@41devs/naya-id-js)
15
+ - **Site** : https://naya.41devs.com
16
+
17
+ ---
18
+
19
+ ## Installation
20
+
21
+ ### Par balise `<script>` (le plus simple)
22
+
23
+ Aucune installation. Chargez le SDK depuis un CDN — il expose un objet global
24
+ `NayaId` :
25
+
26
+ ```html
27
+ <script src="https://cdn.jsdelivr.net/npm/@41devs/naya-id-js@0/dist/naya-id.min.js"></script>
28
+ <script>
29
+ document.getElementById('verifier').addEventListener('click', async () => {
30
+ const result = await NayaId.startVerification({
31
+ apiKey: 'naya_live_xxx',
32
+ config: { country: 'BJ' },
33
+ });
34
+ console.log(result.status, result.verificationId, result.ocrData);
35
+ });
36
+ </script>
37
+ ```
38
+
39
+ > Épinglez une version majeure (`@0`) ou exacte (`@0.1.0`) en production. `unpkg`
40
+ > fonctionne de la même façon : `https://unpkg.com/@41devs/naya-id-js`.
41
+
42
+ ### Par npm (bundlers : Vite, webpack, Rollup…)
43
+
44
+ ```bash
45
+ npm install @41devs/naya-id-js
46
+ ```
47
+
48
+ ```ts
49
+ import { startVerification } from '@41devs/naya-id-js';
50
+
51
+ const result = await startVerification({
52
+ apiKey: 'naya_live_xxx',
53
+ config: { country: 'BJ' },
54
+ });
55
+ ```
56
+
57
+ Le SDK est livré en **ESM**, **CommonJS** et **IIFE**, avec les **types
58
+ TypeScript**. Il n'a **aucune dépendance npm** : la détection de vivacité
59
+ (MediaPipe) est chargée à la demande depuis un CDN au moment du selfie.
60
+
61
+ ---
62
+
63
+ ## Prérequis
64
+
65
+ | Élément | Détail |
66
+ |---|---|
67
+ | **Clé API Naya** | Une clé `naya_live_…` ou `naya_test_…` (créée dans le dashboard Naya). |
68
+ | **Entreprise vérifiée (KYB)** | La société propriétaire de la clé doit être **KYB-vérifiée**, sinon l'API renvoie `403`. |
69
+ | **HTTPS** | La caméra (`getUserMedia`) exige un **contexte sécurisé** : `https://` en production, ou `http://localhost` en développement. Sur `http://` distant, la caméra ne démarre pas. |
70
+ | **CORS** | L'origine de votre page doit être autorisée côté API Naya (liste `ALLOWED_ORIGINS`). |
71
+
72
+ ---
73
+
74
+ ## Démarrage rapide
75
+
76
+ `startVerification` retourne une promesse qui se résout quand l'utilisateur
77
+ ressort du parcours, avec le résultat.
78
+
79
+ ```js
80
+ const result = await NayaId.startVerification({
81
+ apiKey: 'naya_live_xxx',
82
+ config: {
83
+ country: 'BJ', // pré-sélectionne le pays
84
+ callbackUrl: 'https://votre-backend.com/naya/hook', // notification serveur (optionnel)
85
+ },
86
+ });
87
+
88
+ switch (result.status) {
89
+ case 'approved': // Identité vérifiée. result.ocrData contient les champs extraits.
90
+ break;
91
+ case 'review': // Revue manuelle : décision finale notifiée plus tard via callbackUrl.
92
+ break;
93
+ case 'rejected': // Refus (result.note explique pourquoi).
94
+ break;
95
+ case 'cancelled': // L'utilisateur a fermé le parcours avant la fin.
96
+ break;
97
+ case 'error': // Problème technique : result.errorMessage.
98
+ console.error(result.errorMessage);
99
+ break;
100
+ }
101
+ ```
102
+
103
+ `startVerification` **ne rejette jamais** : une erreur réseau ou une annulation
104
+ revient toujours sous forme de `NayaResult` (`status: 'error'` ou `'cancelled'`).
105
+
106
+ ---
107
+
108
+ ## Le parcours vu par l'utilisateur
109
+
110
+ Une fois `startVerification` appelée, l'interface Naya enchaîne seule :
111
+
112
+ 1. **Bienvenue** — écran d'accueil.
113
+ 2. **Sélection** — pays et type de document (sautée si `country` + un seul `documentTypes`).
114
+ 3. **Capture du document** — recto, puis verso pour la CNI (bande MRZ). Cadre vert quand l'image est nette ; l'utilisateur déclenche.
115
+ 4. **Vivacité** — selfie avec défi actif : regarder droit → cligner → tourner la tête.
116
+ 5. **Traitement** — OCR, biométrie, décision côté serveur.
117
+ 6. **Résultat** — approuvé / en revue / rejeté, puis retour à votre page.
118
+
119
+ L'interface est montée dans un **Shadow DOM** : ni le CSS de votre page
120
+ n'affecte le SDK, ni l'inverse.
121
+
122
+ ---
123
+
124
+ ## API
125
+
126
+ ### `NayaId.startVerification(params)`
127
+
128
+ | Champ | Type | Rôle |
129
+ |---|---|---|
130
+ | `apiKey` | `string` | Clé d'application, transmise depuis le navigateur. |
131
+ | `sessionToken` | `string` | Jeton de session éphémère obtenu par votre backend (à privilégier). |
132
+ | `auth` | `{ apiKey?, sessionToken? }` | Alternative aux deux champs ci-dessus, en objet. |
133
+ | `config` | `NayaConfig` | Configuration optionnelle (voir ci-dessous). |
134
+
135
+ ### Configuration (`config`)
136
+
137
+ Tous les champs sont optionnels — le SDK a des défauts raisonnables (tous pays,
138
+ tous documents, français).
139
+
140
+ | Champ | Type | Défaut | Rôle |
141
+ |---|---|---|---|
142
+ | `country` | `'BJ'\|'CI'\|'SN'\|'ML'\|'BF'\|'TG'` | — | Pré-sélectionne le pays ; l'utilisateur ne le choisit plus. |
143
+ | `documentTypes` | `('cni'\|'passeport'\|'permis')[]` | les trois | Types proposés. Un seul + `country` défini ⇒ écran de sélection sauté. |
144
+ | `countries` | `NayaCountryCode[]` | les six | Pays proposés quand `country` n'est pas fixé. |
145
+ | `locale` | `'fr' \| 'en'` | `'fr'` | Langue de l'interface. |
146
+ | `callbackUrl` | `string` | — | URL HTTPS notifiée par le serveur Naya à chaque décision. |
147
+ | `maxAttempts` | `number` | `2` | Nombre d'essais autorisés après un rejet (bouton « Réessayer »). |
148
+ | `theme` | `{ primaryColor?, accentColor? }` | navy / ambre | Surcharge des couleurs de marque. |
149
+ | `clientLogoUrl` | `string` | — | Logo affiché à côté de « Propulsé par Naya ID ». |
150
+ | `livenessAssets` | `{ esmUrl?, wasmBaseUrl?, modelUrl? }` | CDN public | Auto-hébergement MediaPipe (CSP stricte — voir plus bas). |
151
+ | `baseUrl` | `string` | API de prod | À ne changer que pour un environnement de test. |
152
+
153
+ ### Résultat (`NayaResult`)
154
+
155
+ | Champ | Type | Présent quand |
156
+ |---|---|---|
157
+ | `status` | `'approved'\|'review'\|'rejected'\|'cancelled'\|'error'` | toujours |
158
+ | `verificationId` | `string` | dès qu'une session a été créée — à conserver pour rapprocher notifications et dashboard |
159
+ | `riskScore` | `number` (0-100) | après traitement — score de confiance agrégé |
160
+ | `matchScore` | `number` (0-100) | concordance visage document ↔ selfie |
161
+ | `authenticityScore` | `number` (0-100) | intégrité du document |
162
+ | `livenessScore` | `number` (0-100) | vivacité |
163
+ | `ocrConfidence` | `number` (0-100) | confiance de l'OCR |
164
+ | `ocrData` | `NayaOcrData` | champs extraits (nom, prénoms, date de naissance, n° document, NPI…) |
165
+ | `note` | `string` | note d'analyse du pipeline |
166
+ | `errorMessage` | `string` | uniquement si `status === 'error'` |
167
+
168
+ ---
169
+
170
+ ## Authentification
171
+
172
+ Un seul des deux champs est à renseigner :
173
+
174
+ | Mode | Usage |
175
+ |---|---|
176
+ | `apiKey` | Simple, mais la clé est **lisible dans le navigateur** (outils de dev). À réserver aux démos et aux intégrations où le front est maîtrisé. |
177
+ | `sessionToken` | Jeton éphémère obtenu par **votre backend**, limité à une vérification. **À privilégier en production** : la clé ne quitte jamais votre serveur. |
178
+
179
+ ```js
180
+ // Production : votre backend échange sa clé contre un jeton de session.
181
+ await NayaId.startVerification({
182
+ sessionToken: jetonObtenuDeVotreBackend,
183
+ config: { country: 'BJ' },
184
+ });
185
+ ```
186
+
187
+ Passer de l'un à l'autre ne change rien au reste de l'intégration.
188
+
189
+ ---
190
+
191
+ ## Notifications serveur (`callbackUrl`)
192
+
193
+ En passant `callbackUrl`, votre backend est notifié à chaque décision, sans
194
+ interroger l'API. Deux événements possibles par session :
195
+ `verification.completed` (décision automatique) puis, le cas échéant,
196
+ `verification.reviewed` (après revue d'un analyste). Les envois sont signés
197
+ (`X-Naya-Signature`, HMAC-SHA256). Détails côté documentation de l'API.
198
+
199
+ ---
200
+
201
+ ## Détection de vivacité
202
+
203
+ La vivacité s'exécute **sur l'appareil** via [MediaPipe Face Landmarker]. Le
204
+ module et le modèle sont chargés depuis un CDN public **au moment du selfie**
205
+ seulement — aucune configuration requise, et rien n'alourdit le chargement
206
+ initial de votre page. L'analyse est **locale** : la photo n'est transmise qu'à
207
+ la fin du défi.
208
+
209
+ Pour une page à **CSP stricte** (hôtes externes bloqués), auto-hébergez ces
210
+ ressources et indiquez leurs URL :
211
+
212
+ ```js
213
+ config: {
214
+ country: 'BJ',
215
+ livenessAssets: {
216
+ esmUrl: '/assets/naya/vision_bundle.mjs', // module @mediapipe/tasks-vision
217
+ wasmBaseUrl: '/assets/naya/wasm', // dossier wasm/ du paquet
218
+ modelUrl: '/assets/naya/face_landmarker.task', // modèle MediaPipe
219
+ },
220
+ }
221
+ ```
222
+
223
+ Copiez `vision_bundle.mjs`, le dossier `wasm/` et le modèle
224
+ `face_landmarker.task` de `@mediapipe/tasks-vision@1.0.1`.
225
+
226
+ [MediaPipe Face Landmarker]: https://ai.google.dev/edge/mediapipe/solutions/vision/face_landmarker
227
+
228
+ ---
229
+
230
+ ## Contraintes navigateur
231
+
232
+ - **Contexte sécurisé obligatoire** pour la caméra : `https://` ou `http://localhost`.
233
+ - Testé sur Chrome et Safari (desktop et iOS). Les webviews in-app (Instagram,
234
+ Facebook…) restreignent souvent `getUserMedia` — orientez l'utilisateur vers
235
+ le navigateur système si la caméra ne démarre pas.
236
+ - L'interface s'ouvre en superposition plein écran (Shadow DOM) et fige le
237
+ défilement de la page tant que le parcours est ouvert.
238
+
239
+ ---
240
+
241
+ ## Dépannage
242
+
243
+ | Symptôme | Cause probable |
244
+ |---|---|
245
+ | `status: 'error'`, « clé API invalide » (401) | Clé absente/révoquée, ou coquille. |
246
+ | `status: 'error'`, « KYB… » (403) | La société de la clé n'est pas KYB-vérifiée. |
247
+ | `status: 'error'`, « CORS » | L'origine de votre page n'est pas dans `ALLOWED_ORIGINS` côté API. |
248
+ | La caméra ne démarre pas | Page servie en `http://` distant (contexte non sécurisé), permission refusée, ou webview in-app. |
249
+ | Score de vivacité anormalement bas | Selfie de mauvaise qualité (contre-jour, reflets d'écran) — l'anti-fraude serveur le pénalise. |
250
+
251
+ ---
252
+
253
+ ## Développement (ce dépôt)
254
+
255
+ ```bash
256
+ npm install # Node 18+ (22 recommandé)
257
+ npm run build # produit dist/ (iife .min.js, esm .mjs, cjs .cjs, types .d.ts)
258
+ npm run typecheck # vérification TypeScript
259
+ ```
260
+
261
+ Démo locale : après `npm run build`, servez la racine et ouvrez
262
+ `example/index.html` sur `http://localhost` (contexte sécurisé requis) :
263
+
264
+ ```bash
265
+ npm run serve # puis http://localhost:3000/example/
266
+ ```
267
+
268
+ ---
269
+
270
+ ## Licence
271
+
272
+ Propriété de 41Devs. Voir le fichier `LICENSE`.