@ollaid/native-sso 1.0.8 → 2.1.2

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/README.md CHANGED
@@ -22,11 +22,12 @@ Package NPM Frontend-First pour l'authentification Native SSO Ollaid.
22
22
  13. [Migration Laravel](#migration-laravel)
23
23
  14. [Flux d'authentification](#flux-dauthentification)
24
24
  15. [Session & localStorage](#session--localstorage)
25
- 16. [OnboardingModal](#onboardingmodal)
26
- 17. [useTokenHealthCheck](#usetokenhealthcheck)
27
- 18. [Sécurité](#sécurité)
28
- 19. [Exports](#exports)
29
- 20. [Publication & Installation npm](#publication--installation-npm)
25
+ 16. [Déconnexion synchronisée](#déconnexion-synchronisée)
26
+ 17. [OnboardingModal](#onboardingmodal)
27
+ 18. [useTokenHealthCheck](#usetokenhealthcheck)
28
+ 19. [Sécurité](#sécurité)
29
+ 20. [Exports](#exports)
30
+ 21. [Publication & Installation npm](#publication--installation-npm)
30
31
 
31
32
  ---
32
33
 
@@ -155,21 +156,30 @@ déclenche la redirection côté frontend :
155
156
 
156
157
  ## Déconnexion externe (sans le composant)
157
158
 
158
- Quand l'utilisateur se déconnecte **depuis votre SaaS** (ex : bouton logout dans le backoffice), le composant `NativeSSOPage` n'est pas impliqué. Vous devez donc nettoyer manuellement la session SSO pour éviter que le package considère l'utilisateur comme encore connecté.
159
+ Quand l'utilisateur se déconnecte **depuis votre SaaS** (ex : bouton logout dans le backoffice), le composant `NativeSSOPage` n'est pas impliqué. Vous devez utiliser la fonction `logout()` du package pour garantir une déconnexion complète et synchronisée.
160
+
161
+ > ⚠️ **RÈGLE OBLIGATOIRE** : Toute déconnexion frontend **DOIT** passer par `logout()`. Ne jamais effacer le `localStorage` manuellement ni utiliser `clearAuthToken()` seul — cela laisserait des sessions orphelines actives sur l'IAM.
159
162
 
160
163
  ### Utilisation
161
164
 
162
165
  ```tsx
163
- import { clearAuthToken } from '@ollaid/native-sso';
166
+ import { logout } from '@ollaid/native-sso';
164
167
 
165
168
  const handleLogout = async () => {
166
- await revokeBackendToken(); // votre logique SaaS (révocation Sanctum, etc.)
167
- clearAuthToken(); // nettoie les 5 clés localStorage du package
169
+ await logout(); // Double revocation (SaaS + IAM) + nettoyage localStorage
168
170
  navigate('/auth/login');
169
171
  };
170
172
  ```
171
173
 
172
- ### Clés localStorage nettoyées par `clearAuthToken()`
174
+ ### Que fait `logout()` ?
175
+
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
179
+
180
+ Les appels réseau sont en `Promise.allSettled` (best-effort) : même si le serveur est injoignable, le localStorage est **toujours** nettoyé.
181
+
182
+ ### Clés localStorage nettoyées
173
183
 
174
184
  | Clé | Description |
175
185
  |-----|-------------|
@@ -178,8 +188,23 @@ const handleLogout = async () => {
178
188
  | `user` | Objet utilisateur (avec `iam_reference`, `alias_reference`) |
179
189
  | `account_type` | Type de compte (`user` ou `client`) |
180
190
  | `alias_reference` | Référence de l'alias de connexion |
191
+ | `app_access_token_ref` | Référence de l'`AppAccessToken` IAM (pour revocation optimisée) |
192
+
193
+ ### ⛔ `clearAuthToken()` est déprécié
194
+
195
+ `clearAuthToken()` ne fait que vider le localStorage **sans révoquer les sessions** côté SaaS et IAM. Son utilisation seule crée des sessions orphelines.
196
+
197
+ ```tsx
198
+ // ❌ NE PAS FAIRE — sessions orphelines sur l'IAM
199
+ import { clearAuthToken } from '@ollaid/native-sso';
200
+ clearAuthToken();
201
+
202
+ // ✅ FAIRE — double revocation garantie
203
+ import { logout } from '@ollaid/native-sso';
204
+ await logout();
205
+ ```
181
206
 
182
- > **Important :** Si vous ne nettoyez pas ces clés, l'utilisateur verra l'écran "Déconnexion" au lieu du formulaire de connexion en revenant sur la page SSO.
207
+ > **Important :** Si vous ne déconnectez pas proprement, l'utilisateur verra l'écran "Déconnexion" au lieu du formulaire de connexion en revenant sur la page SSO.
183
208
 
184
209
  ---
185
210
 
@@ -707,6 +732,7 @@ $secretKey = $payload['secret_key'];
707
732
  "success": true,
708
733
  "token": "1|abc123def456ghi789...",
709
734
  "expires_at": "2026-04-23T03:12:32.000000Z",
735
+ "app_access_token_ref": "aat_ref_XXXXXXXX",
710
736
  "user": {
711
737
  "id": 1,
712
738
  "reference": "USR-XXXXXXXX",
@@ -766,11 +792,15 @@ Route::post('/native/exchange', function (Request $request) {
766
792
  // Générer un token Sanctum
767
793
  $token = $user->createToken('native-sso')->plainTextToken;
768
794
 
795
+ // Récupérer app_access_token_ref depuis la réponse IAM
796
+ $appAccessTokenRef = $response->json('user_infos.app_access_token_ref');
797
+
769
798
  return response()->json([
770
- 'success' => true,
771
- 'token' => $token,
772
- 'expires_at' => now()->addDays(30)->toISOString(),
773
- 'user' => [
799
+ 'success' => true,
800
+ 'token' => $token,
801
+ 'expires_at' => now()->addDays(30)->toISOString(),
802
+ 'app_access_token_ref' => $appAccessTokenRef,
803
+ 'user' => [
774
804
  'id' => $user->id,
775
805
  'reference' => $user->reference,
776
806
  'alias_reference' => $user->alias_reference,
@@ -790,16 +820,15 @@ Route::post('/native/exchange', function (Request $request) {
790
820
 
791
821
  ### `POST /api/native/check-token`
792
822
 
793
- Vérifie la validité du token Sanctum et retourne les `user_infos` fraîches. Appelé périodiquement par le package (2 min après login, puis toutes les 5 min).
823
+ Vérifie la validité du token Sanctum et retourne les infos utilisateur fraîches. Appelé périodiquement par le package (60 secondes après login, puis toutes les 2 minutes).
794
824
 
795
825
  **Headers requis :** `Authorization: Bearer {token}`
796
826
 
797
827
  **Réponse succès (200) :**
798
828
  ```json
799
829
  {
800
- "success": true,
801
- "valid": true,
802
- "user_infos": {
830
+ "status": "connected",
831
+ "user": {
803
832
  "name": "John Doe",
804
833
  "email": "john@example.com",
805
834
  "ccphone": "+221",
@@ -821,9 +850,8 @@ Route::post('/native/check-token', function (Request $request) {
821
850
  $user = $request->user();
822
851
 
823
852
  return response()->json([
824
- 'success' => true,
825
- 'valid' => true,
826
- 'user_infos' => [
853
+ 'status' => 'connected',
854
+ 'user' => [
827
855
  'name' => $user->name,
828
856
  'email' => $user->email,
829
857
  'ccphone' => $user->ccphone,
@@ -1024,11 +1052,15 @@ class NativeAuthController extends Controller
1024
1052
  $expiresAt = now()->addDays(30);
1025
1053
  $token = $user->createToken('native-sso', ['*'], $expiresAt);
1026
1054
 
1055
+ // Récupérer app_access_token_ref depuis la réponse IAM
1056
+ $appAccessTokenRef = $userInfos['app_access_token_ref'] ?? null;
1057
+
1027
1058
  return response()->json([
1028
- 'success' => true,
1029
- 'token' => $token->plainTextToken,
1030
- 'expires_at' => $expiresAt->toIso8601String(),
1031
- 'user' => [
1059
+ 'success' => true,
1060
+ 'token' => $token->plainTextToken,
1061
+ 'expires_at' => $expiresAt->toIso8601String(),
1062
+ 'app_access_token_ref' => $appAccessTokenRef,
1063
+ 'user' => [
1032
1064
  'id' => $user->id,
1033
1065
  'reference' => $iamReference,
1034
1066
  'alias_reference' => $aliasReference,
@@ -1083,9 +1115,8 @@ class NativeAuthController extends Controller
1083
1115
  $user = $request->user();
1084
1116
 
1085
1117
  return response()->json([
1086
- 'success' => true,
1087
- 'valid' => true,
1088
- 'user_infos' => [
1118
+ 'status' => 'connected',
1119
+ 'user' => [
1089
1120
  'name' => $user->name,
1090
1121
  'email' => $user->email,
1091
1122
  'ccphone' => $user->ccphone,
@@ -1099,21 +1130,43 @@ class NativeAuthController extends Controller
1099
1130
  }
1100
1131
 
1101
1132
  // ════════════════════════════════════════
1102
- // POST /api/native/logout
1133
+ // POST /api/native/logout (déconnexion synchronisée)
1103
1134
  // ════════════════════════════════════════
1104
1135
 
1105
1136
  /**
1106
- * Révoque UNIQUEMENT le token Sanctum courant (single-session).
1137
+ * Révoque le token Sanctum courant (single-session)
1138
+ * ET notifie l'IAM pour révoquer l'AppAccessToken lié.
1107
1139
  * Route protégée par auth:sanctum.
1108
1140
  */
1109
1141
  public function logout(Request $request): JsonResponse
1110
1142
  {
1111
- $request->user()->currentAccessToken()->delete();
1112
-
1113
- return response()->json([
1114
- 'success' => true,
1115
- 'message' => 'Déconnexion réussie',
1116
- ]);
1143
+ try {
1144
+ $user = $request->user();
1145
+
1146
+ if ($user) {
1147
+ $sanctumTokenPlain = $request->bearerToken();
1148
+ $user->currentAccessToken()?->delete();
1149
+
1150
+ // Notifier l'IAM (fire-and-forget, timeout 5s)
1151
+ if ($sanctumTokenPlain) {
1152
+ $iamPrefix = $request->attributes->get('iam_prefix', 'iam');
1153
+ $iamApiUrl = config("services.{$iamPrefix}.api_url", 'https://identityam.ollaid.com/api');
1154
+ try {
1155
+ $appAccessTokenRef = $user->currentAccessToken()->app_access_token_ref ?? null;
1156
+ Http::timeout(5)->post("{$iamApiUrl}/iam/disconnect", array_filter([
1157
+ 'sanctum_token' => $sanctumTokenPlain,
1158
+ 'app_access_token_ref' => $appAccessTokenRef,
1159
+ ]));
1160
+ } catch (\Exception $e) {
1161
+ Log::warning("[NativeSSO] IAM disconnect failed (non-blocking)", ['error' => $e->getMessage()]);
1162
+ }
1163
+ }
1164
+ }
1165
+
1166
+ return response()->json(['success' => true, 'message' => 'Déconnexion réussie']);
1167
+ } catch (\Exception $e) {
1168
+ return response()->json(['success' => true, 'message' => 'Déconnexion effectuée']);
1169
+ }
1117
1170
  }
1118
1171
  }
1119
1172
  ```
@@ -1830,7 +1883,7 @@ Toutes les APIs IAM retournent le même objet `user_infos` avec exactement **9 c
1830
1883
 
1831
1884
  ## Session & localStorage
1832
1885
 
1833
- Le package utilise **5 clés** dans `localStorage` pour persister la session :
1886
+ Le package utilise **6 clés** dans `localStorage` pour persister la session :
1834
1887
 
1835
1888
  | Clé | Contenu | Source | Valeurs possibles |
1836
1889
  |-----|---------|--------|-------------------|
@@ -1839,6 +1892,7 @@ Le package utilise **5 clés** dans `localStorage` pour persister la session :
1839
1892
  | `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":"...",...}` |
1840
1893
  | `account_type` | Type de compte | Déterminé lors du `exchange` selon le mode d'inscription | `"user"` (défaut) ou `"client"` (inscription phone-only) |
1841
1894
  | `alias_reference` | Référence alias utilisée lors de la connexion | Réponse de `/api/native/exchange` (champ `user.alias_reference`) | `"ALI-XXXXXXXX"` |
1895
+ | `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"` |
1842
1896
 
1843
1897
  > **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`.
1844
1898
 
@@ -1850,7 +1904,7 @@ L'objet `user` stocké en localStorage contient deux champs ajoutés automatique
1850
1904
 
1851
1905
  ### Nettoyage
1852
1906
 
1853
- Lors du `logout()`, les 5 clés sont supprimées via `clearAuthToken()`.
1907
+ Lors du `logout()`, les 6 clés sont supprimées via `clearAuthToken()`.
1854
1908
 
1855
1909
  ### Accès programmatique
1856
1910
 
@@ -1865,7 +1919,127 @@ const aliasRef = localStorage.getItem('alias_reference'); // string | null
1865
1919
 
1866
1920
  ---
1867
1921
 
1868
- ## OnboardingModal
1922
+ ## Déconnexion synchronisée
1923
+
1924
+ Le package implémente une **double revocation** ultra-fiable : il contacte **en parallèle** le SaaS **et** l'IAM pour garantir la révocation de toutes les sessions.
1925
+
1926
+ ### Architecture — Double Revocation
1927
+
1928
+ ```
1929
+ ┌─────────────────┐
1930
+ │ Package │──────────────────────────────────┐
1931
+ │ logout() │ │
1932
+ └──────┬──────────┘ │
1933
+ │ ① POST /native/logout │ ② POST /iam/disconnect
1934
+ │ (Sanctum token) │ (sanctum_token + app_access_token_ref)
1935
+ ▼ ▼
1936
+ ┌─────────────────┐ ┌─────────────────┐
1937
+ │ SaaS API │──③ fire-and-forget──────▶│ IAM API │
1938
+ │ │ POST /iam/disconnect │ │
1939
+ └─────────────────┘ └─────────────────┘
1940
+ ```
1941
+
1942
+ **Trois niveaux de sécurité :**
1943
+
1944
+ | Scénario | Résultat |
1945
+ |----------|----------|
1946
+ | ✅ Tout fonctionne | SaaS + IAM révoquent tous les deux |
1947
+ | ⚠️ SaaS injoignable | L'IAM révoque quand même via l'appel direct ② |
1948
+ | ⚠️ IAM injoignable | Le SaaS révoque via ① puis retente via ③ |
1949
+ | ❌ Les deux injoignables | localStorage nettoyé, le health check détectera le 401 plus tard |
1950
+
1951
+ ### Comportement automatique
1952
+
1953
+ Quand `useNativeAuth.logout()` est appelé (ou que l'utilisateur clique "Se déconnecter" dans `NativeSSOPage`) :
1954
+
1955
+ 1. **Appels parallèles** (via `Promise.allSettled`, jamais bloquants) :
1956
+ - `POST /api/native/logout` → SaaS supprime le Sanctum token + notifie l'IAM (fire-and-forget)
1957
+ - `POST /api/iam/disconnect` → IAM révoque directement l'`AppAccessToken` via `sanctum_token` + `app_access_token_ref` (lookup optimisé)
1958
+ 2. **Nettoyage local garanti** : les 6 clés localStorage sont supprimées (`token`, `auth_token`, `user`, `account_type`, `alias_reference`, `app_access_token_ref`)
1959
+ 3. **Aucun blocage** : même si les deux appels échouent (offline, timeout), la déconnexion locale est instantanée
1960
+
1961
+ > **Fiabilité** : `Promise.allSettled` + `.catch()` sur chaque appel. L'appel IAM a un timeout court (5s) pour ne jamais ralentir l'UX.
1962
+
1963
+ ### API IAM de revocation — `POST /api/iam/disconnect`
1964
+
1965
+ Le package contacte directement l'IAM pour révoquer l'`AppAccessToken` lié à la session.
1966
+
1967
+ | Paramètre | Type | Description |
1968
+ |-----------|------|-------------|
1969
+ | `sanctum_token` | `string` | Le token Sanctum stocké localement, utilisé pour identifier l'`AppAccessToken` à révoquer |
1970
+ | `app_access_token_ref` | `string` | (recommandé) Référence directe de l'`AppAccessToken` IAM — lookup instantané par PK |
1971
+
1972
+ 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.
1973
+
1974
+ ### Déconnexion externe (hors package)
1975
+
1976
+ Si votre application gère une déconnexion **en dehors** de `NativeSSOPage` (ex : backoffice, bouton custom) :
1977
+
1978
+ > ⚠️ **OBLIGATOIRE** : utilisez `logout()` — jamais `clearAuthToken()` seul.
1979
+
1980
+ ```ts
1981
+ import { logout } from '@ollaid/native-sso';
1982
+
1983
+ // Double revocation complète (SaaS + IAM) + nettoyage localStorage
1984
+ await logout();
1985
+
1986
+ // Rediriger vers la page de connexion
1987
+ navigate('/auth/login');
1988
+ ```
1989
+
1990
+ `logout()` effectue automatiquement :
1991
+ 1. `POST /api/native/logout` → révoque le Sanctum token
1992
+ 2. `POST /api/iam/disconnect` → révoque l'AppAccessToken IAM (avec `sanctum_token` + `app_access_token_ref`)
1993
+ 3. `clearAuthToken()` → nettoie les 6 clés localStorage
1994
+
1995
+ ### Hook `useLogout()` (recommandé pour React)
1996
+
1997
+ Pour une intégration React avec gestion d'état (loading, error) et callbacks :
1998
+
1999
+ ```tsx
2000
+ import { useLogout } from '@ollaid/native-sso';
2001
+ import { useNavigate } from 'react-router-dom';
2002
+
2003
+ const LogoutButton = () => {
2004
+ const navigate = useNavigate();
2005
+ const { logout, loading, error } = useLogout({
2006
+ onSuccess: () => navigate('/auth/login'),
2007
+ onError: (err) => console.error('Logout failed:', err.message),
2008
+ });
2009
+
2010
+ return (
2011
+ <>
2012
+ <button onClick={logout} disabled={loading}>
2013
+ {loading ? 'Déconnexion...' : 'Se déconnecter'}
2014
+ </button>
2015
+ {error && <p className="text-red-500">{error}</p>}
2016
+ </>
2017
+ );
2018
+ };
2019
+ ```
2020
+
2021
+ | Propriété | Type | Description |
2022
+ |-----------|------|-------------|
2023
+ | **Options** | | |
2024
+ | `onSuccess` | `() => void` | Appelé après déconnexion réussie (redirection, toast, etc.) |
2025
+ | `onError` | `(error: Error) => void` | Appelé en cas d'échec |
2026
+ | **Retour** | | |
2027
+ | `logout` | `() => Promise<void>` | Déclenche la double revocation complète |
2028
+ | `loading` | `boolean` | `true` pendant l'appel |
2029
+ | `error` | `string \| null` | Message d'erreur ou `null` |
2030
+
2031
+ ### Détection automatique des sessions révoquées
2032
+
2033
+ 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.
2034
+
2035
+ > **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.
2036
+
2037
+ ### Prérequis backend
2038
+
2039
+ 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))
2040
+ 2. L'IAM **doit** exposer `POST /api/iam/disconnect` acceptant `{ sanctum_token, app_access_token_ref }` pour la revocation directe par le package
2041
+
2042
+ ---
1869
2043
 
1870
2044
  Modal post-connexion qui invite l'utilisateur à compléter les informations manquantes de son profil.
1871
2045
 
@@ -1928,42 +2102,55 @@ import { OnboardingModal } from '@ollaid/native-sso';
1928
2102
 
1929
2103
  ## useTokenHealthCheck
1930
2104
 
1931
- Hook qui vérifie périodiquement la validité du token Sanctum via `POST /api/native/check-token`.
2105
+ Hook qui vérifie périodiquement la validité du token Sanctum via `POST /api/native/check-token` du SaaS.
2106
+
2107
+ **C'est le package qui gère tout automatiquement** — le SaaS doit juste exposer l'endpoint auth check (voir [BACKEND_INTEGRATION.md](./BACKEND_INTEGRATION.md)).
1932
2108
 
1933
2109
  ### Timing
1934
2110
 
1935
2111
  | Événement | Délai |
1936
2112
  |-----------|-------|
1937
- | Premier check après login | **2 minutes** |
1938
- | Checks suivants | **toutes les 5 minutes** |
1939
- | Requêtes par heure | ~12 |
2113
+ | Premier check après login | **60 secondes** |
2114
+ | Checks suivants | **toutes les 2 minutes** |
2115
+ | Requêtes par heure | ~30 |
1940
2116
 
1941
2117
  ### Comportement réseau
1942
2118
 
1943
2119
  | Situation | Action |
1944
2120
  |-----------|--------|
1945
- | ✅ 200 OK | Met à jour `user_infos` en localStorage |
1946
- | ❌ 401 Unauthorized | Déconnecte l'utilisateur (token expiré/révoqué) |
2121
+ | ✅ 200 `status: 'connected'` | Met à jour `user_infos` en localStorage |
2122
+ | ❌ 401 `status: 'disconnected'` | Révoque l'IAM + déconnecte le frontend |
1947
2123
  | ⚠️ Erreur réseau / timeout / 500 | **Aucune action** — la session est conservée |
1948
2124
  | 📴 Appareil hors ligne | Le check échoue silencieusement, session conservée |
1949
2125
 
2126
+ ### Revocation automatique sur 401
2127
+
2128
+ Quand le SaaS retourne un 401 (token révoqué par un admin, session expirée, etc.) :
2129
+
2130
+ 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
2131
+ 2. **Nettoyage localStorage** — Les 6 clés de session sont supprimées
2132
+ 3. **Réinitialisation de l'état** — L'interface revient à l'écran de connexion
2133
+
1950
2134
  > **Philosophie** : Ne déconnecter que sur un rejet explicite du serveur (401). Jamais sur un problème réseau.
1951
2135
 
1952
- ### Callbacks
2136
+ ### Format de réponse attendu du SaaS
1953
2137
 
1954
- ```ts
1955
- useTokenHealthCheck({
1956
- enabled: true, // Activer/désactiver
1957
- onUserUpdate: (user) => {
1958
- // Appelé quand user_infos sont rafraîchies
1959
- },
1960
- onSessionExpired: () => {
1961
- // Appelé sur 401 — rediriger vers login
1962
- navigate('/auth/sso');
1963
- },
1964
- });
2138
+ ```json
2139
+ // Connecté (200)
2140
+ {
2141
+ "status": "connected",
2142
+ "user": { "name": "...", "email": "...", "avatar": "..." }
2143
+ }
2144
+
2145
+ // Déconnecté (401)
2146
+ {
2147
+ "status": "disconnected",
2148
+ "message": "Utilisateur non connecté"
2149
+ }
1965
2150
  ```
1966
2151
 
2152
+ > Voir [BACKEND_INTEGRATION.md — Section 2.3](./BACKEND_INTEGRATION.md) pour l'implémentation PHP complète.
2153
+
1967
2154
  ---
1968
2155
 
1969
2156
  ## Sécurité
@@ -2035,6 +2222,7 @@ if (config('services.iam.debug')) {
2035
2222
  - `useMobilePassword` — Hook récupération mot de passe
2036
2223
  - `useMobileRegistration` — Hook inscription
2037
2224
  - `useTokenHealthCheck` — Hook vérification périodique du token
2225
+ - `useLogout` — Hook déconnexion sécurisée avec callbacks
2038
2226
 
2039
2227
  ### Provider
2040
2228
  - `NativeSSOProvider` — Provider React pour configuration centralisée
@@ -2045,6 +2233,7 @@ if (config('services.iam.debug')) {
2045
2233
  - `mobilePasswordService` — Service mot de passe
2046
2234
  - `setNativeAuthConfig` — Configuration manuelle des URLs
2047
2235
  - `iamAccountService` — Service APIs IAM Account (link-phone, link-email, refresh-user-info, update-avatar, reset-avatar)
2236
+ - `logout` — Déconnexion complète (double révocation SaaS + IAM + nettoyage localStorage)
2048
2237
  - `getAuthToken` — Récupérer le token depuis localStorage
2049
2238
  - `getAuthUser` — Récupérer l'utilisateur depuis localStorage
2050
2239
  - `getAccountType` — Récupérer le type de compte depuis localStorage
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Hook React pour la déconnexion sécurisée.
3
+ *
4
+ * Wrape `logout()` avec gestion d'état (loading, error)
5
+ * et callbacks (onSuccess, onError) pour une intégration UX simple.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { useLogout } from '@ollaid/native-sso';
10
+ *
11
+ * const LogoutButton = () => {
12
+ * const { logout, loading, error } = useLogout({
13
+ * onSuccess: () => navigate('/auth/login'),
14
+ * onError: (err) => toast.error(err.message),
15
+ * });
16
+ *
17
+ * return (
18
+ * <button onClick={logout} disabled={loading}>
19
+ * {loading ? 'Déconnexion...' : 'Se déconnecter'}
20
+ * </button>
21
+ * );
22
+ * };
23
+ * ```
24
+ *
25
+ * @version 1.0.0
26
+ */
27
+ export interface UseLogoutOptions {
28
+ /** Callback appelé après une déconnexion réussie (redirection, toast, etc.) */
29
+ onSuccess?: () => void;
30
+ /** Callback appelé en cas d'erreur (notification, log, etc.) */
31
+ onError?: (error: Error) => void;
32
+ }
33
+ export interface UseLogoutReturn {
34
+ /** Déclenche la déconnexion complète (double revocation SaaS + IAM) */
35
+ logout: () => Promise<void>;
36
+ /** `true` pendant l'appel de déconnexion */
37
+ loading: boolean;
38
+ /** Message d'erreur si la déconnexion a échoué, `null` sinon */
39
+ error: string | null;
40
+ }
41
+ export declare const useLogout: (options?: UseLogoutOptions) => UseLogoutReturn;
@@ -1,12 +1,16 @@
1
1
  /**
2
- * Hook de vérification périodique du token Sanctum
2
+ * Hook de vérification périodique du token Sanctum (Auth Check)
3
3
  *
4
- * - Premier check 2 min après login
5
- * - Checks suivants toutes les 5 min
4
+ * Le package consulte l'endpoint POST /api/native/check-token du SaaS
5
+ * pour vérifier si l'utilisateur est toujours connecté.
6
+ *
7
+ * - Premier check 60s après login
8
+ * - Checks suivants toutes les 2 min
9
+ * - Si status === 'connected' → met à jour user_infos en localStorage
10
+ * - Si 401 → révoque l'IAM (POST /iam/disconnect) + nettoie le frontend
6
11
  * - Ne déconnecte PAS si offline ou serveur inaccessible
7
- * - Déconnecte UNIQUEMENT si le backend retourne 401 (token invalide)
8
12
  *
9
- * @version 1.0.0
13
+ * @version 2.0.0
10
14
  */
11
15
  import type { UserInfos } from '../types/native';
12
16
  export interface UseTokenHealthCheckOptions {
@@ -14,6 +18,8 @@ export interface UseTokenHealthCheckOptions {
14
18
  enabled: boolean;
15
19
  /** SaaS API base URL */
16
20
  saasApiUrl: string;
21
+ /** IAM API base URL (for revocation on 401) */
22
+ iamApiUrl: string;
17
23
  /** Called when the backend explicitly invalidates the token (401) */
18
24
  onTokenInvalid: () => void;
19
25
  /** Called when fresh user_infos are received */