@ollaid/native-sso 2.1.5 → 2.6.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.
Files changed (41) hide show
  1. package/README.md +398 -63
  2. package/dist/components/AvatarCropModal.d.ts +16 -0
  3. package/dist/components/DebugPanel.d.ts +7 -2
  4. package/dist/components/LoginModal.d.ts +2 -2
  5. package/dist/components/NativeSSOPage.d.ts +11 -3
  6. package/dist/components/OnboardingModal.d.ts +14 -7
  7. package/dist/components/PasswordRecoveryModal.d.ts +1 -1
  8. package/dist/components/PhoneInput.d.ts +2 -1
  9. package/dist/components/SignupModal.d.ts +1 -1
  10. package/dist/components/ui.d.ts +4 -2
  11. package/dist/hooks/useLogout.d.ts +1 -1
  12. package/dist/hooks/useMobilePassword.d.ts +1 -1
  13. package/dist/hooks/useMobileRegistration.d.ts +2 -2
  14. package/dist/hooks/useNativeAuth.d.ts +8 -5
  15. package/dist/hooks/useTokenHealthCheck.d.ts +11 -1
  16. package/dist/index-Bpixveaz.js +489 -0
  17. package/dist/index-Bpixveaz.js.map +1 -0
  18. package/dist/index-DDOXM37y.cjs +488 -0
  19. package/dist/index-DDOXM37y.cjs.map +1 -0
  20. package/dist/index.cjs +9231 -414
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.ts +8 -5
  23. package/dist/index.js +9232 -415
  24. package/dist/index.js.map +1 -1
  25. package/dist/provider.d.ts +4 -1
  26. package/dist/services/api.d.ts +76 -7
  27. package/dist/services/debugLogger.d.ts +1 -1
  28. package/dist/services/iamAccount.d.ts +1 -1
  29. package/dist/services/mobilePassword.d.ts +1 -1
  30. package/dist/services/mobileRegistration.d.ts +1 -1
  31. package/dist/services/nativeAuth.d.ts +3 -2
  32. package/dist/services/profile.d.ts +31 -0
  33. package/dist/services/profileChange.d.ts +30 -0
  34. package/dist/services/profileMedia.d.ts +16 -0
  35. package/dist/types/mobile.d.ts +1 -1
  36. package/dist/types/native.d.ts +23 -1
  37. package/dist/web-BQDVoI6q.cjs +146 -0
  38. package/dist/web-BQDVoI6q.cjs.map +1 -0
  39. package/dist/web-DPmAPlXS.js +146 -0
  40. package/dist/web-DPmAPlXS.js.map +1 -0
  41. package/package.json +11 -4
package/README.md CHANGED
@@ -21,13 +21,16 @@ Package NPM Frontend-First pour l'authentification Native SSO Ollaid.
21
21
  12. [Configuration .env Laravel](#configuration-env-laravel)
22
22
  13. [Migration Laravel](#migration-laravel)
23
23
  14. [Flux d'authentification](#flux-dauthentification)
24
- 15. [Session & localStorage](#session--localstorage)
24
+ 15. [Session & storage](#session--storage)
25
25
  16. [Déconnexion synchronisée](#déconnexion-synchronisée)
26
26
  17. [OnboardingModal](#onboardingmodal)
27
27
  18. [useTokenHealthCheck](#usetokenhealthcheck)
28
28
  19. [Sécurité](#sécurité)
29
29
  20. [Exports](#exports)
30
- 21. [Publication & Installation npm](#publication--installation-npm)
30
+ 21. [Publication & Installation npm](#publication--installation-npm)
31
+ 22. [Webhooks & Health Check (Backend)](#webhooks--health-check-backend)
32
+ 23. [Migration sécurité Web / Capacitor](#migration-sécurité-web--capacitor)
33
+ 24. [Métadonnées device](#métadonnées-device)
31
34
 
32
35
  ---
33
36
 
@@ -37,6 +40,69 @@ Package NPM Frontend-First pour l'authentification Native SSO Ollaid.
37
40
  npm install @ollaid/native-sso
38
41
  ```
39
42
 
43
+ ### Compatibilité Capacitor
44
+
45
+ Le package détecte les métadonnées device via `@capacitor/device` quand l'app consommatrice est une app Capacitor.
46
+
47
+ - Capacitor 7: installez `@capacitor/device@^7.x`
48
+ - Capacitor 8: installez `@capacitor/device@^8.x`
49
+
50
+ Le package déclare `@capacitor/device` comme **peer dependency optionnelle**. Il ne force donc pas une version unique pour tous les SaaS intégrateurs.
51
+
52
+ Par défaut, le package chiffre les données persistées dans `localStorage` avec un wrapper interne. Si votre app fournit un `storage` natif plus robuste, il sera utilisé à la place.
53
+
54
+ ## Métadonnées device
55
+
56
+ Sur les nouvelles connexions, le package envoie automatiquement au backend SaaS les informations utiles pour identifier la session:
57
+
58
+ - `X-Device-Id`
59
+ - `X-Session-UUID`
60
+ - `X-Device-Name`
61
+ - `X-Device-Model`
62
+ - `X-Device-Manufacturer`
63
+ - `X-Device-Operating-System`
64
+ - `X-Device-Os-Version`
65
+ - `X-Device-Platform`
66
+ - `X-Device-Browser`
67
+ - `X-Device-Language`
68
+ - `X-Device-WebView-Version`
69
+ - `X-Device-Is-Native`
70
+ - `X-Device-Label`
71
+
72
+ Ces données servent à enrichir l'affichage backoffice et à rendre les sessions plus lisibles. Elles ne remplacent pas la géolocalisation IP côté backend, qui reste responsable de `city / region / country`.
73
+
74
+ ## Migration sécurité Web / Capacitor
75
+
76
+ Cette section répond à la question la plus importante pour une montée de version en production :
77
+
78
+ - **Installer le nouveau package suffit-il ?**
79
+ - **Oui** pour les changements purement frontend qui gardent le même contrat API.
80
+ - **Non** pour les durcissements de sécurité qui changent le stockage de session, les cookies, ou la synchronisation backend.
81
+
82
+ - **Faut-il aussi modifier le SaaS ?**
83
+ - **Oui** si vous voulez appliquer les recommandations de sécurité de cet audit.
84
+ - **Oui** également si vous voulez une vraie stratégie différente entre **Web** et **Capacitor**.
85
+
86
+ ### Règle simple
87
+
88
+ | Cas | Action |
89
+ |---|---|
90
+ | Mise à jour UI / corrections sans changement de contrat | Installer la nouvelle version du package suffit généralement |
91
+ | Sécurité Web avec cookies HttpOnly | Il faut aussi mettre à jour le backend SaaS |
92
+ | Sécurité Capacitor / mobile natif | Il faut adapter le SaaS et l'app mobile, pas seulement le package |
93
+ | Anti-rejeu webhook / durcissement IAM | Il faut modifier le backend SaaS et parfois l'IAM |
94
+
95
+ ### Recommandation de déploiement
96
+
97
+ 1. Garder la version actuelle compatible en prod.
98
+ 2. Ajouter les migrations backend requises avant d'activer les nouvelles règles de sécurité.
99
+ 3. Déployer le nouveau package sur un environnement de test.
100
+ 4. Vérifier séparément les parcours :
101
+ - Web
102
+ - Capacitor iOS
103
+ - Capacitor Android
104
+ 5. Basculer en production seulement après validation des webhooks, logout et refresh.
105
+
40
106
  ---
41
107
 
42
108
  ## Intégration rapide (3 étapes)
@@ -59,7 +125,7 @@ function App() {
59
125
  return (
60
126
  <Routes>
61
127
  <Route
62
- path="/auth/sso"
128
+ path="/auth/native-sso"
63
129
  element={
64
130
  <NativeSSOPage
65
131
  saasApiUrl="https://mon-saas.com/api"
@@ -69,7 +135,7 @@ function App() {
69
135
  console.log('Connecté !', user.name);
70
136
  navigate('/dashboard');
71
137
  }}
72
- onLogout={() => navigate('/auth/sso')}
138
+ onLogout={() => navigate('/auth/native-sso')}
73
139
  />
74
140
  }
75
141
  />
@@ -81,7 +147,7 @@ function App() {
81
147
 
82
148
  ### 3. C'est tout ✅
83
149
 
84
- La page `/auth/sso` gère automatiquement :
150
+ La page `/auth/native-sso` gère automatiquement :
85
151
  - ✅ Connexion par email (mot de passe + OTP)
86
152
  - ✅ Connexion par téléphone (SMS OTP)
87
153
  - ✅ Connexion par code d'accès
@@ -89,7 +155,7 @@ La page `/auth/sso` gère automatiquement :
89
155
  - ✅ Récupération de mot de passe
90
156
  - ✅ Grant access (inscription auto à une nouvelle app)
91
157
  - ✅ 2FA (TOTP)
92
- - ✅ Session persistée en localStorage
158
+ - ✅ Session persistée via le storage configuré, par défaut `localStorage`
93
159
  - ✅ Branding Ollaid SSO
94
160
 
95
161
  ---
@@ -100,19 +166,23 @@ La page `/auth/sso` gère automatiquement :
100
166
  |------|------|--------|-------------|
101
167
  | `saasApiUrl` | `string` | ✅ | URL du backend SaaS (ex: `https://mon-saas.com/api`) |
102
168
  | `iamApiUrl` | `string` | ✅ | URL du backend IAM (ex: `https://identityam.ollaid.com/api`) |
103
- | `accountType` | `'user' \| 'client'` | ❌ | Type de compte à persister dans localStorage (défaut: `'user'`). Utile si vous avez plusieurs pages SSO avec des rôles différents. |
169
+ | `accountType` | `'user' \| 'client'` | ❌ | Type de compte à persister dans le storage configuré (défaut: `'user'`). Utile si vous avez plusieurs pages SSO avec des rôles différents. |
104
170
  | `configPrefix` | `string` | ❌ | **Multi-tenant** : préfixe de configuration IAM côté backend (défaut: `'iam'`). Permet à un même backend SaaS de gérer N applications IAM. Voir [Multi-Tenant](#multi-tenant-plusieurs-applications-sur-le-même-backend). |
105
171
  | `onLoginSuccess` | `(token: string, user: UserInfos) => void` | ❌ | Callback après connexion réussie |
106
172
  | `onLogout` | `() => void` | ❌ | Callback après déconnexion |
107
- | `title` | `string` | ❌ | Titre personnalisé (défaut: "Un compte, plusieurs accès") |
173
+ | `storage` | `NativeStorageAdapter` | ❌ | Stockage injecté pour WebView / Capacitor / secure storage natif. Si absent, le package utilise un `localStorage` chiffré puis un fallback mémoire. |
174
+ | `title` | `string` | ❌ | Titre personnalisé (défaut: "Un compte pour toutes vos applications") |
108
175
  | `description` | `string` | ❌ | Description personnalisée |
109
176
  | `logoUrl` | `string` | ❌ | URL du logo (remplace le slider) |
110
177
  | `hideFooter` | `boolean` | ❌ | Masquer "Propulsé par iam.ollaid.com" |
111
- | `onOnboardingComplete` | `(data: { image_url?: string; ccphone?: string; phone?: string }) => void` | ❌ | Callback après complétion de l'onboarding |
178
+ | `onOnboardingComplete` | `(data: { name?: string; image_url?: string; ccphone?: string; phone?: string; email?: string }) => void` | ❌ | Callback après complétion de l'onboarding |
112
179
  | `redirectAfterLogin` | `string` | ❌ | Route vers laquelle rediriger après connexion réussie (ex: `/client/dashboard`). Utilise `window.location.href`. Compatible avec ou sans react-router. |
113
180
  | `redirectAfterLogout` | `string` | ❌ | Route vers laquelle rediriger après déconnexion (ex: `/auth/client`). Utilise `window.location.href`. |
114
181
 
115
182
  > **Note :** Le mode `debug` est contrôlé **uniquement** par le backend via la variable d'environnement `IAM_DEBUG` dans le `.env` du SaaS. Il n'y a plus de prop `debug` à passer au composant. Le `DebugPanel` est **réactif** : il apparaît automatiquement après le chargement des credentials si `debug: true` est retourné par le backend.
183
+ > Quand le `DebugPanel` est visible, il expose aussi des boutons de test pour ouvrir `Connexion`, `Inscription`, `Infos profile` et un reset du rappel profil.
184
+ >
185
+ > **Note Capacitor** : si votre app mobile utilise un secure storage natif, fournissez un adapter via `storage` afin d'aller au-delà du `localStorage` chiffré par défaut du package.
116
186
 
117
187
  ### Redirections automatiques (optionnel)
118
188
 
@@ -173,9 +243,9 @@ const handleLogout = async () => {
173
243
 
174
244
  ### Que fait `logout()` ?
175
245
 
176
- 1. **Révoque le token SaaS** — `POST /api/native/logout` (supprime le Sanctum token)
177
- 2. **Révoque la session IAM** — `POST /api/iam/disconnect` (avec `sanctum_token` + `app_access_token_ref`)
178
- 3. **Nettoie le localStorage** — supprime les 6 clés du package
246
+ 1. **Révoque la session SaaS** — `POST /api/native/logout`
247
+ 2. **Révoque la session IAM** — `POST /api/iam/disconnect` (avec `app_access_token_ref`)
248
+ 3. **Nettoie le stockage local** — supprime les clés de session du package
179
249
 
180
250
  Les appels réseau sont en `Promise.allSettled` (best-effort) : même si le serveur est injoignable, le localStorage est **toujours** nettoyé.
181
251
 
@@ -183,12 +253,15 @@ Les appels réseau sont en `Promise.allSettled` (best-effort) : même si le serv
183
253
 
184
254
  | Clé | Description |
185
255
  |-----|-------------|
186
- | `auth_token` | Token Sanctum actif |
256
+ | `auth_token` | Token de session SaaS actif |
187
257
  | `token` | Token legacy |
188
258
  | `user` | Objet utilisateur (avec `iam_reference`, `alias_reference`) |
189
259
  | `account_type` | Type de compte (`user` ou `client`) |
190
260
  | `alias_reference` | Référence de l'alias de connexion |
191
261
  | `app_access_token_ref` | Référence de l'`AppAccessToken` IAM (pour revocation optimisée) |
262
+ | `refresh_token` | Refresh token SaaS (si activé) |
263
+ | `token_expires_at` | Expiration du token Sanctum (si fournie) |
264
+ | `refresh_expires_at` | Expiration du refresh token (si fournie) |
192
265
 
193
266
  ### ⛔ `clearAuthToken()` est déprécié
194
267
 
@@ -235,7 +308,7 @@ Backend SaaS:
235
308
  configPrefix="iam"
236
309
  accountType="user"
237
310
  redirectAfterLogin="/dashboard"
238
- redirectAfterLogout="/auth/sso"
311
+ redirectAfterLogout="/auth/native-sso"
239
312
  />
240
313
 
241
314
  {/* Page login espace vendeur */}
@@ -329,7 +402,7 @@ return [
329
402
 
330
403
  ### Côté Backend SaaS — Controller multi-tenant
331
404
 
332
- Tous les controllers Native (`config`, `exchange`, `check-token`, `logout`) doivent lire le header :
405
+ Tous les controllers Native (`config`, `exchange`, `check-token`, `refresh`, `logout`) doivent lire le header :
333
406
 
334
407
  ```php
335
408
  class NativeConfigController extends Controller
@@ -374,7 +447,29 @@ class NativeConfigController extends Controller
374
447
  }
375
448
  ```
376
449
 
377
- > **⚠️ Important** : Appliquez la même logique `X-IAM-Config-Prefix` dans `exchange`, `check-token` et `logout`.
450
+ > **⚠️ Important** : Appliquez la même logique `X-IAM-Config-Prefix` dans `exchange`, `check-token`, `refresh` et `logout`.
451
+
452
+ ## Device metadata & session identity
453
+
454
+ Le package envoie automatiquement des headers de contexte device sur les requêtes d'authentification et de session :
455
+
456
+ - `X-Device-Id` (stable par appareil / webview)
457
+ - `X-Session-UUID` (UUID stable par instance, pour différencier plusieurs sessions sur un même device)
458
+ - `X-Device-Name`
459
+ - `X-Device-Model`
460
+ - `X-Device-Manufacturer`
461
+ - `X-Device-Operating-System`
462
+ - `X-Device-Os-Version`
463
+ - `X-Device-Platform`
464
+ - `X-Device-Browser`
465
+ - `X-Device-Language`
466
+ - `X-Device-WebView-Version`
467
+ - `X-Device-Is-Native`
468
+ - `X-Device-Label`
469
+
470
+ Ces valeurs servent à enrichir la session IAM et l'affichage backoffice. Elles sont utiles pour la traçabilité, mais ne doivent pas être utilisées comme preuve d'identité ou comme facteur d'autorisation à elles seules.
471
+
472
+ > **Note** : la géolocalisation `city / region / country` n'est pas fournie par le package. Elle est calculée côté backend à partir de l'IP observée.
378
473
 
379
474
  ---
380
475
 
@@ -415,6 +510,14 @@ php artisan config:clear
415
510
  php artisan config:cache
416
511
  ```
417
512
 
513
+ ### Sécurité et stockage
514
+
515
+ - Les métadonnées device sont du contexte, pas des secrets.
516
+ - Les tokens d'authentification doivent rester dans un stockage adapté à votre plateforme.
517
+ - Sur Capacitor, utilisez un secure storage natif si vous manipulez des données sensibles longue durée.
518
+ - Ne déduisez jamais les droits utilisateur à partir des seuls headers device.
519
+ - Le package fournit désormais un socle fiable d'identité de session pour les nouvelles connexions; les seules variations restantes viennent du contexte réseau ou du terminal exposé par l'environnement, pas du flux SSO lui-même.
520
+
418
521
  ---
419
522
 
420
523
  ## Usage avancé (composants individuels)
@@ -618,7 +721,8 @@ import { useMobileRegistration } from '@ollaid/native-sso';
618
721
 
619
722
  ## Backend SaaS — Endpoints requis
620
723
 
621
- Le backend SaaS (Laravel) doit exposer **4 endpoints**. Voici les spécifications exactes :
724
+ Le backend SaaS (Laravel) doit exposer **5 endpoints** : `config`, `exchange`, `check-token`, `refresh`, `logout`.
725
+ Voici les spécifications exactes :
622
726
 
623
727
  ### `GET /api/native/config`
624
728
 
@@ -826,6 +930,7 @@ Vérifie la validité du token Sanctum et retourne les infos utilisateur fraîch
826
930
  **Réponse succès (200) :**
827
931
  ```json
828
932
  {
933
+ "success": true,
829
934
  "status": "connected",
830
935
  "user": {
831
936
  "name": "John Doe",
@@ -849,6 +954,7 @@ Route::post('/native/check-token', function (Request $request) {
849
954
  $user = $request->user();
850
955
 
851
956
  return response()->json([
957
+ 'success' => true,
852
958
  'status' => 'connected',
853
959
  'user' => [
854
960
  'name' => $user->name,
@@ -864,7 +970,46 @@ Route::post('/native/check-token', function (Request $request) {
864
970
  })->middleware('auth:sanctum');
865
971
  ```
866
972
 
867
- > **Comportement réseau :** Le package ne déconnecte l'utilisateur que si le backend retourne explicitement **401**. En cas d'erreur réseau, timeout ou serveur inaccessible, la session est conservée.
973
+ > **Règle de déconnexion :** Le package ne déconnecte l'utilisateur **que** sur un **HTTP 401 explicite**. Tout HTTP 200 (quelle que soit la structure du body) confirme la session. Erreur réseau, timeout, 5xx, offline → session conservée, jamais de déconnexion inutile.
974
+
975
+ ---
976
+
977
+ ### `POST /api/native/refresh` (OBLIGATOIRE pour la stabilité)
978
+
979
+ Renouvelle la session **sans déconnecter**. Le package l'utilise :
980
+ - en **proactif** (avant expiration, si `expires_at` est fourni)
981
+ - en **récupération** quand `check-token` retourne `401` (tente un refresh avant logout)
982
+
983
+ **Body :**
984
+ ```json
985
+ { "refresh_token": "rt_..." }
986
+ ```
987
+
988
+ **Recommandation (stabilité maximale) :** ne pas changer le token Sanctum.
989
+ Le refresh doit **prolonger `expires_at`** du token existant (le client garde son token actuel).
990
+
991
+ **Réponse succès (200) :**
992
+ ```json
993
+ {
994
+ "success": true,
995
+ "expires_at": "2026-06-13T12:00:00+00:00",
996
+ "refresh_token": "rt_new_...",
997
+ "refresh_expires_at": "2026-08-13T12:00:00+00:00",
998
+ "user": { "name": "John Doe" }
999
+ }
1000
+ ```
1001
+
1002
+ **Réponse refresh invalide (401) :**
1003
+ ```json
1004
+ {
1005
+ "success": false,
1006
+ "error_type": "invalid_refresh",
1007
+ "message": "Refresh token invalide ou expiré"
1008
+ }
1009
+ ```
1010
+
1011
+ > Si le refresh est invalide (`401 invalid_refresh`) : c'est une révocation explicite, la déconnexion est autorisée.
1012
+ > Si offline/timeout/5xx : ne jamais déconnecter.
868
1013
 
869
1014
  ---
870
1015
 
@@ -900,7 +1045,7 @@ Route::post('/native/logout', function (Request $request) {
900
1045
 
901
1046
  ## Controller Laravel Complet (copier-coller)
902
1047
 
903
- Voici un `NativeAuthController.php` complet regroupant les 4 endpoints. Copiez-le dans `app/Http/Controllers/Api/NativeAuthController.php` :
1048
+ Voici un `NativeAuthController.php` complet regroupant les 5 endpoints. Copiez-le dans `app/Http/Controllers/Api/NativeAuthController.php` :
904
1049
 
905
1050
  ```php
906
1051
  <?php
@@ -1122,6 +1267,7 @@ class NativeAuthController extends Controller
1122
1267
  $user = $request->user();
1123
1268
 
1124
1269
  return response()->json([
1270
+ 'success' => true,
1125
1271
  'status' => 'connected',
1126
1272
  'message' => 'Utilisateur connecté',
1127
1273
  'user' => [
@@ -1152,7 +1298,6 @@ class NativeAuthController extends Controller
1152
1298
  $user = $request->user();
1153
1299
 
1154
1300
  if ($user) {
1155
- $sanctumTokenPlain = $request->bearerToken();
1156
1301
  $currentToken = $user->currentAccessToken();
1157
1302
  $appAccessTokenRef = $currentToken?->app_access_token_ref ?? null;
1158
1303
 
@@ -1160,14 +1305,13 @@ class NativeAuthController extends Controller
1160
1305
  $currentToken?->delete();
1161
1306
 
1162
1307
  // Notifier l'IAM (fire-and-forget, timeout 5s)
1163
- if ($sanctumTokenPlain || $appAccessTokenRef) {
1308
+ if ($appAccessTokenRef) {
1164
1309
  $iamPrefix = $request->attributes->get('iam_prefix', 'iam');
1165
1310
  $iamApiUrl = config("services.{$iamPrefix}.api_url", 'https://identityam.ollaid.com/api');
1166
1311
  try {
1167
- Http::timeout(5)->post("{$iamApiUrl}/iam/disconnect", array_filter([
1168
- 'sanctum_token' => $sanctumTokenPlain,
1312
+ Http::timeout(5)->post("{$iamApiUrl}/iam/disconnect", [
1169
1313
  'app_access_token_ref' => $appAccessTokenRef,
1170
- ]));
1314
+ ]);
1171
1315
  } catch (\Exception $e) {
1172
1316
  Log::warning("[NativeSSO] IAM disconnect failed (non-blocking)", ['error' => $e->getMessage()]);
1173
1317
  }
@@ -1340,6 +1484,14 @@ public function up(): void
1340
1484
 
1341
1485
  > **Note :** Le champ `reference` est l'identifiant unique IAM de l'utilisateur. Le champ `alias_reference` identifie l'utilisateur dans le contexte d'une application spécifique.
1342
1486
 
1487
+ ### Colonnes recommandées sur `personal_access_tokens` (Sanctum)
1488
+
1489
+ Pour la révocation synchronisée et la stabilité de session :
1490
+ - `app_access_token_ref` (révocation IAM ciblée)
1491
+ - `refresh_token_hash` + `refresh_expires_at` (refresh token)
1492
+
1493
+ Ces migrations sont détaillées dans `BACKEND_INTEGRATION.md` du package.
1494
+
1343
1495
  ---
1344
1496
 
1345
1497
  ## Flux d'authentification
@@ -1384,7 +1536,7 @@ public function up(): void
1384
1536
  4. **Validate** — Le frontend envoie le mot de passe/OTP, l'IAM retourne un `callback_token`
1385
1537
  5. **Exchange** — Le frontend envoie le `callback_token` au backend SaaS, qui le décrypte via l'IAM et crée une session Sanctum
1386
1538
 
1387
- > **Important :** Le package gère les étapes 1-5 automatiquement. Le backend SaaS doit implémenter **4 endpoints** (`config`, `exchange`, `check-token`, `logout`).
1539
+ > **Important :** Le package gère les étapes 1-5 automatiquement. Le backend SaaS doit implémenter **5 endpoints** (`config`, `exchange`, `check-token`, `refresh`, `logout`).
1388
1540
 
1389
1541
  ---
1390
1542
 
@@ -1892,30 +2044,61 @@ Toutes les APIs IAM retournent le même objet `user_infos` avec exactement **9 c
1892
2044
 
1893
2045
  ---
1894
2046
 
1895
- ## Session & localStorage
2047
+ ## Session & storage
1896
2048
 
1897
- Le package utilise **6 clés** dans `localStorage` pour persister la session :
2049
+ Par défaut, le package utilise un petit ensemble de clés préfixées `sso_` dans `localStorage` pour persister la session et le suivi du profil.
2050
+ Vous pouvez injecter un `storage` différent via `NativeSSOPage`, `NativeSSOProvider`, `useNativeAuth` ou `setNativeStorage()`.
1898
2051
 
1899
2052
  | Clé | Contenu | Source | Valeurs possibles |
1900
2053
  |-----|---------|--------|-------------------|
1901
- | `token` | Token Sanctum (bearer) | Réponse de `/api/native/exchange` | Chaîne `"1\|abc123..."` |
1902
- | `auth_token` | Copie du token (compatibilité) | Même source que `token` | Idem |
1903
- | `user` | Objet utilisateur enrichi sérialisé en JSON | Réponse de `/api/native/exchange` ou mise à jour via health check | `{"iam_reference":"USR-XXX","alias_reference":"ALI-XXX","name":"...","email":"...",...}` |
1904
- | `account_type` | Type de compte | Déterminé lors du `exchange` selon le mode d'inscription | `"user"` (défaut) ou `"client"` (inscription phone-only) |
1905
- | `alias_reference` | Référence alias utilisée lors de la connexion | Réponse de `/api/native/exchange` (champ `user.alias_reference`) | `"ALI-XXXXXXXX"` |
1906
- | `app_access_token_ref` | Référence de l'`AppAccessToken` IAM | Réponse de `/api/native/exchange` (champ `app_access_token_ref`) | `"42"` ou `"aat_ref_abc123"` |
1907
-
1908
- > **Note :** `account_type` et `alias_reference` sont stockés **séparément** de l'objet `user` en plus d'être inclus dans le JSON de la clé `user`.
2054
+ | `sso_auth_token` | Token Sanctum (bearer) canonique | Réponse de `/api/native/exchange` | Chaîne `"1\|abc123..."` |
2055
+ | `sso_token` | Alias rétrocompatible en lecture | Même source que `sso_auth_token` | Idem |
2056
+ | `sso_user` | Objet utilisateur enrichi sérialisé en JSON | Réponse de `/api/native/exchange` ou mise à jour via health check | `{"iam_reference":"USR-XXX","alias_reference":"ALI-XXX","name":"...","email":"...",...}` |
2057
+ | `sso_account_type` | Type de compte | Déterminé lors du `exchange` selon le mode d'inscription | `"user"` (défaut) ou `"client"` (inscription phone-only) |
2058
+ | `sso_alias_reference` | Référence alias utilisée lors de la connexion | Réponse de `/api/native/exchange` (champ `user.alias_reference`) | `"ALI-XXXXXXXX"` |
2059
+ | `sso_app_access_token_ref` | Référence de l'`AppAccessToken` IAM | Réponse de `/api/native/exchange` (champ `app_access_token_ref`) | `"42"` ou `"aat_ref_abc123"` |
2060
+ | `sso_device_id` | Identifiant stable de l'appareil | Généré par le package | Chaîne stable |
2061
+ | `sso_session_uuid` | UUID stable de la session | Généré par le package | UUID |
2062
+ | `sso_image_last_status` | Statut du dernier onboarding profil | Mis à jour par `OnboardingModal` | `"true"` ou `"false"` |
2063
+ | `sso_image_last_check` | Timestamp du dernier contrôle profil | Mis à jour par `OnboardingModal` / sync profil | Millisecondes Unix |
2064
+ | `sso_image_recheck_at` | Timestamp de rappel après snooze | Mis à jour quand l'utilisateur passe l'onboarding | Millisecondes Unix |
2065
+
2066
+ > **Note :** `sso_account_type` et `sso_alias_reference` sont stockés **séparément** de l'objet `user` en plus d'être inclus dans le JSON de `sso_user`.
2067
+ > **Note :** `sso_image_last_status`, `sso_image_last_check` et `sso_image_recheck_at` vivent dans le storage configuré du SaaS et servent au rappel d'onboarding du profil.
2068
+ > **Note sécurité :** la règle d’activité doit être gérée côté backend avec `last_active_at`. Si `last_active_at` dépasse 6 mois, la session doit être révoquée côté SaaS puis côté IAM.
1909
2069
 
1910
2070
  #### Champs enrichis dans l'objet `user`
1911
2071
 
1912
- L'objet `user` stocké en localStorage contient deux champs ajoutés automatiquement par le package :
2072
+ L'objet `user` stocké dans `sso_user` contient deux champs ajoutés automatiquement par le package :
1913
2073
  - `iam_reference` : la référence IAM de l'utilisateur (`USR-XXXXXXXX`), extraite de `user.reference`
1914
2074
  - `alias_reference` : la référence de l'alias utilisé lors de la connexion (`ALI-XXXXXXXX`), extraite de `user.alias_reference`
1915
2075
 
1916
2076
  ### Nettoyage
1917
2077
 
1918
- Lors du `logout()`, les 6 clés sont supprimées via `clearAuthToken()`.
2078
+ Lors du `logout()`, les clés de session sont supprimées via `clearAuthToken()`.
2079
+ Les clés de rappel profil (`sso_image_*`) ne sont pas effacées, afin de conserver le comportement de relance après snooze.
2080
+
2081
+ ### API locale pour le frontend hôte
2082
+
2083
+ Quand votre frontend SaaS a besoin d'un état de session, il doit appeler le package au lieu de lire les clés du storage directement.
2084
+
2085
+ ```ts
2086
+ import { getSsoSessionSnapshot } from '@ollaid/native-sso';
2087
+
2088
+ const session = getSsoSessionSnapshot();
2089
+ // {
2090
+ // authToken,
2091
+ // refreshToken,
2092
+ // appAccessTokenRef,
2093
+ // aliasReference,
2094
+ // accountType,
2095
+ // deviceId,
2096
+ // sessionUuid,
2097
+ // user
2098
+ // }
2099
+ ```
2100
+
2101
+ Cette API retourne les valeurs déchiffrées par le package et évite au frontend hôte de dépendre du détail des clés `sso_`.
1919
2102
 
1920
2103
  ### Accès programmatique
1921
2104
 
@@ -1928,6 +2111,28 @@ const type = getAccountType(); // string | null
1928
2111
  const aliasRef = localStorage.getItem('alias_reference'); // string | null
1929
2112
  ```
1930
2113
 
2114
+ ### Accès via l'API locale du package
2115
+
2116
+ Le frontend SaaS doit utiliser l'API locale du package pour récupérer l'état SSO, au lieu de lire le storage directement.
2117
+
2118
+ ```ts
2119
+ import { getSsoSessionSnapshot, hasSsoSession } from '@ollaid/native-sso';
2120
+
2121
+ const session = getSsoSessionSnapshot();
2122
+
2123
+ if (hasSsoSession()) {
2124
+ console.log(session.authToken);
2125
+ console.log(session.refreshToken);
2126
+ console.log(session.appAccessTokenRef);
2127
+ console.log(session.aliasReference);
2128
+ console.log(session.deviceId);
2129
+ console.log(session.sessionUuid);
2130
+ console.log(session.user);
2131
+ }
2132
+ ```
2133
+
2134
+ Cette API retourne les valeurs déchiffrées et garde le détail des clés `sso_` encapsulé dans le package.
2135
+
1931
2136
  ---
1932
2137
 
1933
2138
  ## Déconnexion synchronisée
@@ -1942,7 +2147,7 @@ Le package implémente une **double revocation** ultra-fiable : il contacte **en
1942
2147
  │ logout() │ │
1943
2148
  └──────┬──────────┘ │
1944
2149
  │ ① POST /native/logout │ ② POST /iam/disconnect
1945
- │ (Sanctum token) │ (sanctum_token + app_access_token_ref)
2150
+ │ (Sanctum token) │ (app_access_token_ref)
1946
2151
  ▼ ▼
1947
2152
  ┌─────────────────┐ ┌─────────────────┐
1948
2153
  │ SaaS API │──③ fire-and-forget──────▶│ IAM API │
@@ -1964,9 +2169,9 @@ Le package implémente une **double revocation** ultra-fiable : il contacte **en
1964
2169
  Quand `useNativeAuth.logout()` est appelé (ou que l'utilisateur clique "Se déconnecter" dans `NativeSSOPage`) :
1965
2170
 
1966
2171
  1. **Appels parallèles** (via `Promise.allSettled`, jamais bloquants) :
1967
- - `POST /api/native/logout` → SaaS supprime le Sanctum token + notifie l'IAM (fire-and-forget)
1968
- - `POST /api/iam/disconnect` → IAM révoque directement l'`AppAccessToken` via `sanctum_token` + `app_access_token_ref` (lookup optimisé)
1969
- 2. **Nettoyage local garanti** : les 6 clés localStorage sont supprimées (`token`, `auth_token`, `user`, `account_type`, `alias_reference`, `app_access_token_ref`)
2172
+ - `POST /api/native/logout` → SaaS révoque sa session locale
2173
+ - `POST /api/iam/disconnect` → IAM révoque directement l'`AppAccessToken` via `app_access_token_ref`
2174
+ 2. **Nettoyage local garanti** : les clés de session sont supprimées (`auth_token`, `token`, `user`, `account_type`, `alias_reference`, `app_access_token_ref`)
1970
2175
  3. **Aucun blocage** : même si les deux appels échouent (offline, timeout), la déconnexion locale est instantanée
1971
2176
 
1972
2177
  > **Fiabilité** : `Promise.allSettled` + `.catch()` sur chaque appel. L'appel IAM a un timeout court (5s) pour ne jamais ralentir l'UX.
@@ -1977,10 +2182,9 @@ Le package contacte directement l'IAM pour révoquer l'`AppAccessToken` lié à
1977
2182
 
1978
2183
  | Paramètre | Type | Description |
1979
2184
  |-----------|------|-------------|
1980
- | `sanctum_token` | `string` | Le token Sanctum stocké localement, utilisé pour identifier l'`AppAccessToken` à révoquer |
1981
- | `app_access_token_ref` | `string` | (recommandé) Référence directe de l'`AppAccessToken` IAM — lookup instantané par PK |
2185
+ | `app_access_token_ref` | `string` | Référence directe de l'`AppAccessToken` IAM lookup instantané par PK |
1982
2186
 
1983
- L'IAM cherche d'abord par `app_access_token_ref` (lookup par PK, O(1)), puis fallback sur `sanctum_token` (index), puis `iam_token` (hash). Le passage de `app_access_token_ref` est **recommandé** pour des performances optimales.
2187
+ L'IAM cherche par `app_access_token_ref` (lookup par PK, O(1)).
1984
2188
 
1985
2189
  ### Déconnexion externe (hors package)
1986
2190
 
@@ -2000,8 +2204,8 @@ navigate('/auth/login');
2000
2204
 
2001
2205
  `logout()` effectue automatiquement :
2002
2206
  1. `POST /api/native/logout` → révoque le Sanctum token
2003
- 2. `POST /api/iam/disconnect` → révoque l'AppAccessToken IAM (avec `sanctum_token` + `app_access_token_ref`)
2004
- 3. `clearAuthToken()` → nettoie les 6 clés localStorage
2207
+ 2. `POST /api/iam/disconnect` → révoque l'AppAccessToken IAM via `app_access_token_ref`
2208
+ 3. `clearAuthToken()` → nettoie les clés de session
2005
2209
 
2006
2210
  ### Hook `useLogout()` (recommandé pour React)
2007
2211
 
@@ -2041,18 +2245,18 @@ const LogoutButton = () => {
2041
2245
 
2042
2246
  ### Détection automatique des sessions révoquées
2043
2247
 
2044
- Le [health check](#usetokenhealthcheck) (toutes les 2 min) détecte automatiquement si un token a été révoqué côté SaaS (par un admin, par la limite de sessions, etc.). Si le serveur répond `401`, le package **révoque aussi l'IAM** (`POST /api/iam/disconnect` avec `sanctum_token` + `app_access_token_ref`) puis déconnecte proprement l'utilisateur.
2248
+ Le [health check](#usetokenhealthcheck) (toutes les 2 min) détecte automatiquement si un token a été révoqué côté SaaS (par un admin, par la limite de sessions, etc.). Si le serveur répond `401`, le package **révoque aussi l'IAM** (`POST /api/iam/disconnect` avec `app_access_token_ref`) puis déconnecte proprement l'utilisateur.
2045
2249
 
2046
2250
  > **Important** : seul un `401` explicite déclenche la déconnexion automatique. Les erreurs réseau ou serveur (5xx) ne déclenchent **pas** de déconnexion pour éviter les déconnexions intempestives.
2047
2251
 
2048
2252
  ### Prérequis backend
2049
2253
 
2050
- 1. Votre endpoint `POST /api/native/logout` **doit** notifier l'IAM après avoir supprimé le token Sanctum (voir [BACKEND_INTEGRATION.md](./BACKEND_INTEGRATION.md))
2051
- 2. L'IAM **doit** exposer `POST /api/iam/disconnect` acceptant `{ sanctum_token, app_access_token_ref }` pour la revocation directe par le package
2254
+ 1. Votre endpoint `POST /api/native/logout` **doit** révoquer la session SaaS locale et peut notifier l'IAM de manière best-effort après avoir récupéré `app_access_token_ref` (voir [BACKEND_INTEGRATION.md](./BACKEND_INTEGRATION.md))
2255
+ 2. L'IAM **doit** exposer `POST /api/iam/disconnect` acceptant `{ app_access_token_ref }`
2052
2256
 
2053
2257
  ---
2054
2258
 
2055
- Modal post-connexion qui invite l'utilisateur à compléter les informations manquantes de son profil.
2259
+ Modal post-connexion qui invite l'utilisateur à compléter les informations manquantes de son profil, ou à éditer ses informations complètes depuis un SaaS.
2056
2260
 
2057
2261
  ### Props
2058
2262
 
@@ -2061,35 +2265,102 @@ Modal post-connexion qui invite l'utilisateur à compléter les informations man
2061
2265
  | `open` | `boolean` | ✅ | Contrôle l'ouverture de la modal |
2062
2266
  | `onOpenChange` | `(open: boolean) => void` | ✅ | Callback changement d'état |
2063
2267
  | `user` | `NativeUser` | ✅ | Objet utilisateur courant (pour détecter les champs manquants) |
2268
+ | `variant` | `'missing' \| 'edit'` | ❌ | `missing` affiche uniquement les champs absents, `edit` affiche les champs de base en mode édition complet |
2064
2269
  | `onComplete` | `(data) => void` | ✅ | Callback avec les données saisies |
2065
2270
  | `onSkip` | `() => void` | ✅ | Callback si l'utilisateur passe l'étape |
2066
2271
 
2067
2272
  ### Champs affichés
2068
2273
 
2069
- La modal affiche **uniquement** les champs manquants :
2070
- - **Photo de profil** — si `user.image_url` est vide (max 2 Mo, JPG/PNG)
2071
- - **Numéro de téléphone** si `user.phone` est vide
2072
- - **Adresse email** — si `user.email` est vide (**optionnel**, ne bloque pas la validation)
2274
+ La modal gère **deux modes** :
2275
+
2276
+ - **Mode `missing`** : affiche uniquement les champs manquants
2277
+ - **Nom complet** — si `user.name` est vide
2278
+ - **Photo de profil** — si `user.image_url` est vide (max 2 Mo, JPG/PNG)
2279
+ - **Numéro de téléphone** — si `user.phone` est vide
2280
+ - **Adresse email** — si `user.email` est vide
2281
+ - **Mode `edit`** : affiche les champs de base éditables même si le profil est déjà complet
2282
+ - Nom complet
2283
+ - Photo de profil
2284
+ - Numéro de téléphone en lecture seule avec bouton `Changer téléphone`
2285
+ - Adresse email en lecture seule avec bouton `Changer email`
2073
2286
 
2074
2287
  ### Callback `onComplete`
2075
2288
 
2076
2289
  ```ts
2077
2290
  onComplete: (data: {
2291
+ name?: string; // Nom complet (si fourni ou modifié)
2078
2292
  image_url?: string; // Base64 de la photo (si ajoutée)
2079
2293
  ccphone?: string; // Indicatif (si téléphone ajouté)
2080
2294
  phone?: string; // Numéro (si téléphone ajouté)
2081
- email?: string; // Email (si renseigné — optionnel)
2295
+ email?: string; // Email (si renseigné)
2082
2296
  }) => void;
2083
2297
  ```
2084
2298
 
2085
2299
  ### Condition de soumission
2086
2300
 
2087
2301
  L'utilisateur **doit** :
2088
- 1. Cocher la case de confirmation
2089
- 2. Fournir une photo (si manquante)
2090
- 3. Fournir un téléphone valide (si manquant)
2302
+ 1. Fournir un nom si le nom est affiché
2303
+ 2. Fournir une photo si elle est affichée
2304
+ 3. Fournir un téléphone valide si le champ est affiché
2305
+ 4. Fournir un email valide si le champ est affiché
2306
+
2307
+ Après connexion, la page autonome attend 5 minutes avant d'ouvrir cette modal si le profil est incomplet.
2308
+ Si l'utilisateur la ferme sans compléter, le package enregistre un snooze de 8 heures (`sso_image_last_status=false`, `sso_image_last_check=now`, `sso_image_recheck_at=now+8h`).
2309
+ Un sync profil best-effort relit ensuite l'état utilisateur toutes les 30 minutes et remet `sso_image_last_status=true` dès que le profil est complet.
2310
+
2311
+ ### Mode automatique
2091
2312
 
2092
- L'email est **toujours optionnel** — il n'est pas requis pour valider.
2313
+ Le mode automatique est piloté par `NativeSSOPage` :
2314
+ - la modal s'ouvre après le délai de 5 minutes si des infos sont manquantes,
2315
+ - elle reste sur un écran de chargement tant que l'hydratation IAM n'a pas renvoyé le profil,
2316
+ - elle affiche ensuite uniquement les champs manquants,
2317
+ - si l'utilisateur ferme la modal, le rappel est repoussé de 8 heures.
2318
+
2319
+ ### Mode manuel depuis un SaaS
2320
+
2321
+ Quand vous ouvrez la modal depuis votre SaaS via un bouton "Infos profil" ou "Modifier mon profil", utilisez `variant="edit"`.
2322
+
2323
+ Exemple :
2324
+
2325
+ ```tsx
2326
+ import { useState } from 'react';
2327
+ import { OnboardingModal } from '@ollaid/native-sso';
2328
+
2329
+ export function ProfileButton({ user }: { user: NativeUser }) {
2330
+ const [open, setOpen] = useState(false);
2331
+
2332
+ return (
2333
+ <>
2334
+ <button type="button" onClick={() => setOpen(true)}>
2335
+ Infos profil
2336
+ </button>
2337
+
2338
+ <OnboardingModal
2339
+ open={open}
2340
+ onOpenChange={setOpen}
2341
+ onDismiss={() => setOpen(false)}
2342
+ user={user}
2343
+ variant="edit"
2344
+ profileHydrating={false}
2345
+ onComplete={(data) => {
2346
+ // Mettre à jour votre cache local / localStorage avec data.user_infos
2347
+ setOpen(false);
2348
+ }}
2349
+ onSkip={() => setOpen(false)}
2350
+ />
2351
+ </>
2352
+ );
2353
+ }
2354
+ ```
2355
+
2356
+ Dans ce mode, le changement de téléphone/email ouvre un sous-flux OTP dans la même modal:
2357
+ 1. saisie du nouveau téléphone/email,
2358
+ 2. envoi du premier OTP sur le contact actuel,
2359
+ 3. vérification du premier OTP,
2360
+ 4. envoi d'un second OTP sur le nouveau contact,
2361
+ 5. confirmation finale et mise à jour du profil.
2362
+
2363
+ Les écrans de changement restent en `static backdrop` côté package: un clic hors modal ne ferme pas le flux.
2093
2364
 
2094
2365
  ### Exemple
2095
2366
 
@@ -2100,6 +2371,7 @@ import { OnboardingModal } from '@ollaid/native-sso';
2100
2371
  open={showOnboarding}
2101
2372
  onOpenChange={setShowOnboarding}
2102
2373
  user={currentUser}
2374
+ variant="edit"
2103
2375
  onComplete={async (data) => {
2104
2376
  // Envoyer les données au backend pour mise à jour
2105
2377
  await api.updateProfile(data);
@@ -2138,8 +2410,8 @@ Hook qui vérifie périodiquement la validité du token Sanctum via `POST /api/n
2138
2410
 
2139
2411
  Quand le SaaS retourne un 401 (token révoqué par un admin, session expirée, etc.) :
2140
2412
 
2141
- 1. **Appel IAM** — Le package appelle `POST /api/iam/disconnect` avec `sanctum_token` + `app_access_token_ref` (fire-and-forget, 5s timeout) pour révoquer l'`AppAccessToken` correspondant
2142
- 2. **Nettoyage localStorage** — Les 6 clés de session sont supprimées
2413
+ 1. **Appel IAM** — Le package appelle `POST /api/iam/disconnect` avec `app_access_token_ref` (fire-and-forget, 5s timeout) pour révoquer l'`AppAccessToken` correspondant
2414
+ 2. **Nettoyage localStorage** — Les clés de session sont supprimées
2143
2415
  3. **Réinitialisation de l'état** — L'interface revient à l'écran de connexion
2144
2416
 
2145
2417
  > **Philosophie** : Ne déconnecter que sur un rejet explicite du serveur (401). Jamais sur un problème réseau.
@@ -2207,13 +2479,57 @@ if (config('services.iam.debug')) {
2207
2479
 
2208
2480
  ### Bonnes pratiques
2209
2481
 
2210
- - ✅ Le token Sanctum est stocké en `localStorage` (standard pour SPA)
2482
+ - ✅ Le token Sanctum est stocké via le storage configuré, par défaut `localStorage`
2211
2483
  - ✅ Les credentials IAM sont gardés **en mémoire uniquement** (jamais persistés)
2212
2484
  - ✅ Le device ID est un identifiant aléatoire (pas de fingerprinting invasif)
2213
2485
  - ✅ Le logout est single-session (ne déconnecte pas les autres appareils)
2214
2486
  - ✅ Le health check ne déconnecte que sur 401 explicite
2215
2487
  - ✅ Les photos sont validées à 2 Mo max côté client
2216
- - ✅ Les lectures `localStorage` sont protégées par try/catch
2488
+ - ✅ Les lectures storage sont protégées par try/catch
2489
+
2490
+ ---
2491
+
2492
+ ## Migration sécurité Web / Capacitor
2493
+
2494
+ Cette section résume l'impact réel d'une mise à niveau en production.
2495
+
2496
+ ### Est-ce qu'un simple `npm install` suffit ?
2497
+
2498
+ - **Oui**, si vous ne changez que des comportements frontend compatibles avec le contrat actuel.
2499
+ - **Non**, si vous voulez appliquer les recommandations de sécurité de l'audit ou changer le stockage de session.
2500
+
2501
+ ### Est-ce qu'il faut modifier les SaaS ?
2502
+
2503
+ Oui dans les cas suivants :
2504
+
2505
+ - passage à des cookies HttpOnly côté Web
2506
+ - durcissement du logout et du refresh
2507
+ - ajout de l'anti-rejeu sur les webhooks
2508
+ - durcissement de la gestion des redirections
2509
+ - stratégie différente pour Capacitor / mobile natif
2510
+
2511
+ ### Règle simple
2512
+
2513
+ | Cas | Action |
2514
+ |---|---|
2515
+ | Correction UI sans changement de contrat | Installer la nouvelle version du package suffit généralement |
2516
+ | Sécurité Web | Mettre à jour le package **et** le backend SaaS |
2517
+ | Sécurité Capacitor / mobile | Mettre à jour le package **et** l'application mobile, parfois aussi le backend SaaS |
2518
+ | Anti-rejeu webhook / intégrité crypto | Mettre à jour le backend SaaS et l'IAM |
2519
+
2520
+ ### Compatibilité Web / Capacitor
2521
+
2522
+ - **Web** : `localStorage` fonctionne techniquement, mais ce n'est pas le modèle le plus sûr.
2523
+ - **Capacitor / WebView** : `localStorage` n'est pas un coffre-fort. Si vous avez besoin d'une sécurité plus forte, utilisez un stockage natif sécurisé ou évitez de conserver des secrets longue durée dans le WebView.
2524
+ - **Conclusion** : le package reste compatible avec les deux environnements, mais la stratégie de persistance de session doit être adaptée côté application consommatrice.
2525
+
2526
+ ### Ordre de déploiement recommandé
2527
+
2528
+ 1. Déployer les migrations backend nécessaires.
2529
+ 2. Mettre à jour le package npm.
2530
+ 3. Tester le Web.
2531
+ 4. Tester Capacitor iOS et Android.
2532
+ 5. Activer la nouvelle stratégie de sécurité en production.
2217
2533
 
2218
2534
  ---
2219
2535
 
@@ -2243,11 +2559,17 @@ if (config('services.iam.debug')) {
2243
2559
  - `nativeAuthService` — Service d'authentification
2244
2560
  - `mobilePasswordService` — Service mot de passe
2245
2561
  - `setNativeAuthConfig` — Configuration manuelle des URLs
2562
+ - `getSsoSessionSnapshot` — Snapshot local déchiffré de la session
2563
+ - `hasSsoSession` — Vérifie si une session SSO locale existe
2564
+ - `setNativeStorage` / `getNativeStorage` — Injection du storage session pour WebView / Capacitor / secure storage natif
2246
2565
  - `iamAccountService` — Service APIs IAM Account (link-phone, link-email, refresh-user-info, update-avatar, reset-avatar)
2247
2566
  - `logout` — Déconnexion complète (double révocation SaaS + IAM + nettoyage localStorage)
2248
2567
  - `getAuthToken` — Récupérer le token depuis localStorage
2249
2568
  - `getAuthUser` — Récupérer l'utilisateur depuis localStorage
2250
2569
  - `getAccountType` — Récupérer le type de compte depuis localStorage
2570
+ - `getDeviceId` — Récupérer/générer le `X-Device-Id` (persisté)
2571
+ - `getSessionUuid` — Récupérer/générer le `X-Session-UUID` (persisté)
2572
+ - `STORAGE_KEYS` — Constantes des clés localStorage utilisées par le package
2251
2573
 
2252
2574
  ### Types
2253
2575
  - `UserInfos`, `NativeAuthState`, `NativeAuthStatus`, `NativeCredentials`, etc.
@@ -2352,3 +2674,16 @@ npm publish --access public # 3. Publier sur npm
2352
2674
  ## Licence
2353
2675
 
2354
2676
  Propriétaire — Ollaid © 2026
2677
+
2678
+ ---
2679
+
2680
+ ## Webhooks & Health Check (Backend)
2681
+
2682
+ Pour garantir la fiabilité et la synchronisation en temps réel (ex: révocation instantanée d'une session bannie), votre backend doit implémenter deux briques supplémentaires.
2683
+
2684
+ Consultez le guide détaillé : [**WEBHOOKS_HEALTH.md**](./WEBHOOKS_HEALTH.md)
2685
+
2686
+ | Brique | Rôle |
2687
+ |--------|------|
2688
+ | **Health Check** | Permet à l'IAM de vérifier que votre SaaS est "Healthy" et bien configuré. |
2689
+ | **Webhooks** | Permet à l'IAM de notifier votre SaaS d'événements critiques (suspension, révocation). |