@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 +247 -58
- package/dist/hooks/useLogout.d.ts +41 -0
- package/dist/hooks/useTokenHealthCheck.d.ts +11 -5
- package/dist/index.cjs +150 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +150 -44
- package/dist/index.js.map +1 -1
- package/dist/services/api.d.ts +26 -0
- package/dist/services/nativeAuth.d.ts +1 -1
- package/dist/types/native.d.ts +5 -3
- package/package.json +1 -1
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. [
|
|
26
|
-
17. [
|
|
27
|
-
18. [
|
|
28
|
-
19. [
|
|
29
|
-
20. [
|
|
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
|
|
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 {
|
|
166
|
+
import { logout } from '@ollaid/native-sso';
|
|
164
167
|
|
|
165
168
|
const handleLogout = async () => {
|
|
166
|
-
await
|
|
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
|
-
###
|
|
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
|
|
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'
|
|
771
|
-
'token'
|
|
772
|
-
'expires_at'
|
|
773
|
-
'
|
|
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
|
|
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
|
-
"
|
|
801
|
-
"
|
|
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
|
-
'
|
|
825
|
-
'
|
|
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'
|
|
1029
|
-
'token'
|
|
1030
|
-
'expires_at'
|
|
1031
|
-
'
|
|
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
|
-
'
|
|
1087
|
-
'
|
|
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
|
|
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
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
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 **
|
|
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
|
|
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
|
-
##
|
|
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 | **
|
|
1938
|
-
| Checks suivants | **toutes les
|
|
1939
|
-
| Requêtes par heure | ~
|
|
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
|
|
1946
|
-
| ❌ 401
|
|
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
|
-
###
|
|
2136
|
+
### Format de réponse attendu du SaaS
|
|
1953
2137
|
|
|
1954
|
-
```
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
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
|
-
*
|
|
5
|
-
*
|
|
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
|
|
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 */
|