@ollaid/native-sso 2.1.5 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +174 -25
- package/dist/components/AvatarCropModal.d.ts +16 -0
- package/dist/components/DebugPanel.d.ts +7 -2
- package/dist/components/LoginModal.d.ts +2 -2
- package/dist/components/NativeSSOPage.d.ts +8 -2
- package/dist/components/OnboardingModal.d.ts +14 -7
- package/dist/components/PasswordRecoveryModal.d.ts +1 -1
- package/dist/components/PhoneInput.d.ts +2 -1
- package/dist/components/SignupModal.d.ts +1 -1
- package/dist/components/ui.d.ts +4 -2
- package/dist/hooks/useLogout.d.ts +1 -1
- package/dist/hooks/useMobilePassword.d.ts +1 -1
- package/dist/hooks/useMobileRegistration.d.ts +1 -1
- package/dist/hooks/useNativeAuth.d.ts +1 -1
- package/dist/hooks/useTokenHealthCheck.d.ts +11 -1
- package/dist/index.cjs +2051 -168
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +2052 -169
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts +1 -1
- package/dist/services/api.d.ts +28 -1
- package/dist/services/debugLogger.d.ts +1 -1
- package/dist/services/iamAccount.d.ts +1 -1
- package/dist/services/mobilePassword.d.ts +1 -1
- package/dist/services/mobileRegistration.d.ts +1 -1
- package/dist/services/nativeAuth.d.ts +3 -2
- package/dist/services/profile.d.ts +31 -0
- package/dist/services/profileChange.d.ts +30 -0
- package/dist/services/profileMedia.d.ts +16 -0
- package/dist/types/mobile.d.ts +1 -1
- package/dist/types/native.d.ts +23 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@ Package NPM Frontend-First pour l'authentification Native SSO Ollaid.
|
|
|
28
28
|
19. [Sécurité](#sécurité)
|
|
29
29
|
20. [Exports](#exports)
|
|
30
30
|
21. [Publication & Installation npm](#publication--installation-npm)
|
|
31
|
+
22. [Webhooks & Health Check (Backend)](#webhooks--health-check-backend)
|
|
31
32
|
|
|
32
33
|
---
|
|
33
34
|
|
|
@@ -59,7 +60,7 @@ function App() {
|
|
|
59
60
|
return (
|
|
60
61
|
<Routes>
|
|
61
62
|
<Route
|
|
62
|
-
path="/auth/sso"
|
|
63
|
+
path="/auth/native-sso"
|
|
63
64
|
element={
|
|
64
65
|
<NativeSSOPage
|
|
65
66
|
saasApiUrl="https://mon-saas.com/api"
|
|
@@ -69,7 +70,7 @@ function App() {
|
|
|
69
70
|
console.log('Connecté !', user.name);
|
|
70
71
|
navigate('/dashboard');
|
|
71
72
|
}}
|
|
72
|
-
onLogout={() => navigate('/auth/sso')}
|
|
73
|
+
onLogout={() => navigate('/auth/native-sso')}
|
|
73
74
|
/>
|
|
74
75
|
}
|
|
75
76
|
/>
|
|
@@ -81,7 +82,7 @@ function App() {
|
|
|
81
82
|
|
|
82
83
|
### 3. C'est tout ✅
|
|
83
84
|
|
|
84
|
-
La page `/auth/sso` gère automatiquement :
|
|
85
|
+
La page `/auth/native-sso` gère automatiquement :
|
|
85
86
|
- ✅ Connexion par email (mot de passe + OTP)
|
|
86
87
|
- ✅ Connexion par téléphone (SMS OTP)
|
|
87
88
|
- ✅ Connexion par code d'accès
|
|
@@ -104,15 +105,16 @@ La page `/auth/sso` gère automatiquement :
|
|
|
104
105
|
| `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
106
|
| `onLoginSuccess` | `(token: string, user: UserInfos) => void` | ❌ | Callback après connexion réussie |
|
|
106
107
|
| `onLogout` | `() => void` | ❌ | Callback après déconnexion |
|
|
107
|
-
| `title` | `string` | ❌ | Titre personnalisé (défaut: "Un compte
|
|
108
|
+
| `title` | `string` | ❌ | Titre personnalisé (défaut: "Un compte pour toutes vos applications") |
|
|
108
109
|
| `description` | `string` | ❌ | Description personnalisée |
|
|
109
110
|
| `logoUrl` | `string` | ❌ | URL du logo (remplace le slider) |
|
|
110
111
|
| `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 |
|
|
112
|
+
| `onOnboardingComplete` | `(data: { name?: string; image_url?: string; ccphone?: string; phone?: string; email?: string }) => void` | ❌ | Callback après complétion de l'onboarding |
|
|
112
113
|
| `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
114
|
| `redirectAfterLogout` | `string` | ❌ | Route vers laquelle rediriger après déconnexion (ex: `/auth/client`). Utilise `window.location.href`. |
|
|
114
115
|
|
|
115
116
|
> **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.
|
|
117
|
+
> Quand le `DebugPanel` est visible, il expose aussi des boutons de test pour ouvrir `Connexion`, `Inscription`, `Infos profile` et un reset du rappel profil.
|
|
116
118
|
|
|
117
119
|
### Redirections automatiques (optionnel)
|
|
118
120
|
|
|
@@ -175,7 +177,7 @@ const handleLogout = async () => {
|
|
|
175
177
|
|
|
176
178
|
1. **Révoque le token SaaS** — `POST /api/native/logout` (supprime le Sanctum token)
|
|
177
179
|
2. **Révoque la session IAM** — `POST /api/iam/disconnect` (avec `sanctum_token` + `app_access_token_ref`)
|
|
178
|
-
3. **Nettoie le localStorage** — supprime les
|
|
180
|
+
3. **Nettoie le localStorage** — supprime les clés de session du package
|
|
179
181
|
|
|
180
182
|
Les appels réseau sont en `Promise.allSettled` (best-effort) : même si le serveur est injoignable, le localStorage est **toujours** nettoyé.
|
|
181
183
|
|
|
@@ -189,6 +191,9 @@ Les appels réseau sont en `Promise.allSettled` (best-effort) : même si le serv
|
|
|
189
191
|
| `account_type` | Type de compte (`user` ou `client`) |
|
|
190
192
|
| `alias_reference` | Référence de l'alias de connexion |
|
|
191
193
|
| `app_access_token_ref` | Référence de l'`AppAccessToken` IAM (pour revocation optimisée) |
|
|
194
|
+
| `refresh_token` | Refresh token SaaS (si activé) |
|
|
195
|
+
| `token_expires_at` | Expiration du token Sanctum (si fournie) |
|
|
196
|
+
| `refresh_expires_at` | Expiration du refresh token (si fournie) |
|
|
192
197
|
|
|
193
198
|
### ⛔ `clearAuthToken()` est déprécié
|
|
194
199
|
|
|
@@ -235,7 +240,7 @@ Backend SaaS:
|
|
|
235
240
|
configPrefix="iam"
|
|
236
241
|
accountType="user"
|
|
237
242
|
redirectAfterLogin="/dashboard"
|
|
238
|
-
redirectAfterLogout="/auth/sso"
|
|
243
|
+
redirectAfterLogout="/auth/native-sso"
|
|
239
244
|
/>
|
|
240
245
|
|
|
241
246
|
{/* Page login espace vendeur */}
|
|
@@ -329,7 +334,7 @@ return [
|
|
|
329
334
|
|
|
330
335
|
### Côté Backend SaaS — Controller multi-tenant
|
|
331
336
|
|
|
332
|
-
Tous les controllers Native (`config`, `exchange`, `check-token`, `logout`) doivent lire le header :
|
|
337
|
+
Tous les controllers Native (`config`, `exchange`, `check-token`, `refresh`, `logout`) doivent lire le header :
|
|
333
338
|
|
|
334
339
|
```php
|
|
335
340
|
class NativeConfigController extends Controller
|
|
@@ -374,7 +379,11 @@ class NativeConfigController extends Controller
|
|
|
374
379
|
}
|
|
375
380
|
```
|
|
376
381
|
|
|
377
|
-
> **⚠️ Important** : Appliquez la même logique `X-IAM-Config-Prefix` dans `exchange`, `check-token` et `logout`.
|
|
382
|
+
> **⚠️ Important** : Appliquez la même logique `X-IAM-Config-Prefix` dans `exchange`, `check-token`, `refresh` et `logout`.
|
|
383
|
+
|
|
384
|
+
Le package envoie aussi :
|
|
385
|
+
- `X-Device-Id` (stable par appareil / webview)
|
|
386
|
+
- `X-Session-UUID` (UUID stable par instance, pour différencier plusieurs sessions sur un même device)
|
|
378
387
|
|
|
379
388
|
---
|
|
380
389
|
|
|
@@ -618,7 +627,8 @@ import { useMobileRegistration } from '@ollaid/native-sso';
|
|
|
618
627
|
|
|
619
628
|
## Backend SaaS — Endpoints requis
|
|
620
629
|
|
|
621
|
-
Le backend SaaS (Laravel) doit exposer **
|
|
630
|
+
Le backend SaaS (Laravel) doit exposer **5 endpoints** : `config`, `exchange`, `check-token`, `refresh`, `logout`.
|
|
631
|
+
Voici les spécifications exactes :
|
|
622
632
|
|
|
623
633
|
### `GET /api/native/config`
|
|
624
634
|
|
|
@@ -826,6 +836,7 @@ Vérifie la validité du token Sanctum et retourne les infos utilisateur fraîch
|
|
|
826
836
|
**Réponse succès (200) :**
|
|
827
837
|
```json
|
|
828
838
|
{
|
|
839
|
+
"success": true,
|
|
829
840
|
"status": "connected",
|
|
830
841
|
"user": {
|
|
831
842
|
"name": "John Doe",
|
|
@@ -849,6 +860,7 @@ Route::post('/native/check-token', function (Request $request) {
|
|
|
849
860
|
$user = $request->user();
|
|
850
861
|
|
|
851
862
|
return response()->json([
|
|
863
|
+
'success' => true,
|
|
852
864
|
'status' => 'connected',
|
|
853
865
|
'user' => [
|
|
854
866
|
'name' => $user->name,
|
|
@@ -864,7 +876,46 @@ Route::post('/native/check-token', function (Request $request) {
|
|
|
864
876
|
})->middleware('auth:sanctum');
|
|
865
877
|
```
|
|
866
878
|
|
|
867
|
-
> **
|
|
879
|
+
> **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.
|
|
880
|
+
|
|
881
|
+
---
|
|
882
|
+
|
|
883
|
+
### `POST /api/native/refresh` (OBLIGATOIRE pour la stabilité)
|
|
884
|
+
|
|
885
|
+
Renouvelle la session **sans déconnecter**. Le package l'utilise :
|
|
886
|
+
- en **proactif** (avant expiration, si `expires_at` est fourni)
|
|
887
|
+
- en **récupération** quand `check-token` retourne `401` (tente un refresh avant logout)
|
|
888
|
+
|
|
889
|
+
**Body :**
|
|
890
|
+
```json
|
|
891
|
+
{ "refresh_token": "rt_..." }
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
**Recommandation (stabilité maximale) :** ne pas changer le token Sanctum.
|
|
895
|
+
Le refresh doit **prolonger `expires_at`** du token existant (le client garde son token actuel).
|
|
896
|
+
|
|
897
|
+
**Réponse succès (200) :**
|
|
898
|
+
```json
|
|
899
|
+
{
|
|
900
|
+
"success": true,
|
|
901
|
+
"expires_at": "2026-06-13T12:00:00+00:00",
|
|
902
|
+
"refresh_token": "rt_new_...",
|
|
903
|
+
"refresh_expires_at": "2026-08-13T12:00:00+00:00",
|
|
904
|
+
"user": { "name": "John Doe" }
|
|
905
|
+
}
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
**Réponse refresh invalide (401) :**
|
|
909
|
+
```json
|
|
910
|
+
{
|
|
911
|
+
"success": false,
|
|
912
|
+
"error_type": "invalid_refresh",
|
|
913
|
+
"message": "Refresh token invalide ou expiré"
|
|
914
|
+
}
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
> Si le refresh est invalide (`401 invalid_refresh`) : c'est une révocation explicite, la déconnexion est autorisée.
|
|
918
|
+
> Si offline/timeout/5xx : ne jamais déconnecter.
|
|
868
919
|
|
|
869
920
|
---
|
|
870
921
|
|
|
@@ -900,7 +951,7 @@ Route::post('/native/logout', function (Request $request) {
|
|
|
900
951
|
|
|
901
952
|
## Controller Laravel Complet (copier-coller)
|
|
902
953
|
|
|
903
|
-
Voici un `NativeAuthController.php` complet regroupant les
|
|
954
|
+
Voici un `NativeAuthController.php` complet regroupant les 5 endpoints. Copiez-le dans `app/Http/Controllers/Api/NativeAuthController.php` :
|
|
904
955
|
|
|
905
956
|
```php
|
|
906
957
|
<?php
|
|
@@ -1122,6 +1173,7 @@ class NativeAuthController extends Controller
|
|
|
1122
1173
|
$user = $request->user();
|
|
1123
1174
|
|
|
1124
1175
|
return response()->json([
|
|
1176
|
+
'success' => true,
|
|
1125
1177
|
'status' => 'connected',
|
|
1126
1178
|
'message' => 'Utilisateur connecté',
|
|
1127
1179
|
'user' => [
|
|
@@ -1340,6 +1392,14 @@ public function up(): void
|
|
|
1340
1392
|
|
|
1341
1393
|
> **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
1394
|
|
|
1395
|
+
### Colonnes recommandées sur `personal_access_tokens` (Sanctum)
|
|
1396
|
+
|
|
1397
|
+
Pour la révocation synchronisée et la stabilité de session :
|
|
1398
|
+
- `app_access_token_ref` (révocation IAM ciblée)
|
|
1399
|
+
- `refresh_token_hash` + `refresh_expires_at` (refresh token)
|
|
1400
|
+
|
|
1401
|
+
Ces migrations sont détaillées dans `BACKEND_INTEGRATION.md` du package.
|
|
1402
|
+
|
|
1343
1403
|
---
|
|
1344
1404
|
|
|
1345
1405
|
## Flux d'authentification
|
|
@@ -1384,7 +1444,7 @@ public function up(): void
|
|
|
1384
1444
|
4. **Validate** — Le frontend envoie le mot de passe/OTP, l'IAM retourne un `callback_token`
|
|
1385
1445
|
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
1446
|
|
|
1387
|
-
> **Important :** Le package gère les étapes 1-5 automatiquement. Le backend SaaS doit implémenter **
|
|
1447
|
+
> **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
1448
|
|
|
1389
1449
|
---
|
|
1390
1450
|
|
|
@@ -1894,7 +1954,7 @@ Toutes les APIs IAM retournent le même objet `user_infos` avec exactement **9 c
|
|
|
1894
1954
|
|
|
1895
1955
|
## Session & localStorage
|
|
1896
1956
|
|
|
1897
|
-
Le package utilise **
|
|
1957
|
+
Le package utilise **9 clés** dans `localStorage` pour persister la session et le suivi du profil :
|
|
1898
1958
|
|
|
1899
1959
|
| Clé | Contenu | Source | Valeurs possibles |
|
|
1900
1960
|
|-----|---------|--------|-------------------|
|
|
@@ -1904,8 +1964,12 @@ Le package utilise **6 clés** dans `localStorage` pour persister la session :
|
|
|
1904
1964
|
| `account_type` | Type de compte | Déterminé lors du `exchange` selon le mode d'inscription | `"user"` (défaut) ou `"client"` (inscription phone-only) |
|
|
1905
1965
|
| `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
1966
|
| `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"` |
|
|
1967
|
+
| `sso_image_last_status` | Statut du dernier onboarding profil | Mis à jour par `OnboardingModal` | `"true"` ou `"false"` |
|
|
1968
|
+
| `sso_image_last_check` | Timestamp du dernier contrôle profil | Mis à jour par `OnboardingModal` / sync profil | Millisecondes Unix |
|
|
1969
|
+
| `sso_image_recheck_at` | Timestamp de rappel après snooze | Mis à jour quand l'utilisateur passe l'onboarding | Millisecondes Unix |
|
|
1907
1970
|
|
|
1908
1971
|
> **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`.
|
|
1972
|
+
> **Note :** `sso_image_last_status`, `sso_image_last_check` et `sso_image_recheck_at` vivent dans le `localStorage` du SaaS et servent au rappel d'onboarding du profil.
|
|
1909
1973
|
|
|
1910
1974
|
#### Champs enrichis dans l'objet `user`
|
|
1911
1975
|
|
|
@@ -1915,7 +1979,8 @@ L'objet `user` stocké en localStorage contient deux champs ajoutés automatique
|
|
|
1915
1979
|
|
|
1916
1980
|
### Nettoyage
|
|
1917
1981
|
|
|
1918
|
-
Lors du `logout()`, les 6 clés sont supprimées via `clearAuthToken()`.
|
|
1982
|
+
Lors du `logout()`, les 6 clés de session sont supprimées via `clearAuthToken()`.
|
|
1983
|
+
Les clés de rappel profil (`sso_image_*`) ne sont pas effacées, afin de conserver le comportement de relance après snooze.
|
|
1919
1984
|
|
|
1920
1985
|
### Accès programmatique
|
|
1921
1986
|
|
|
@@ -2052,7 +2117,7 @@ Le [health check](#usetokenhealthcheck) (toutes les 2 min) détecte automatiquem
|
|
|
2052
2117
|
|
|
2053
2118
|
---
|
|
2054
2119
|
|
|
2055
|
-
Modal post-connexion qui invite l'utilisateur à compléter les informations manquantes de son profil.
|
|
2120
|
+
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
2121
|
|
|
2057
2122
|
### Props
|
|
2058
2123
|
|
|
@@ -2061,35 +2126,102 @@ Modal post-connexion qui invite l'utilisateur à compléter les informations man
|
|
|
2061
2126
|
| `open` | `boolean` | ✅ | Contrôle l'ouverture de la modal |
|
|
2062
2127
|
| `onOpenChange` | `(open: boolean) => void` | ✅ | Callback changement d'état |
|
|
2063
2128
|
| `user` | `NativeUser` | ✅ | Objet utilisateur courant (pour détecter les champs manquants) |
|
|
2129
|
+
| `variant` | `'missing' \| 'edit'` | ❌ | `missing` affiche uniquement les champs absents, `edit` affiche les champs de base en mode édition complet |
|
|
2064
2130
|
| `onComplete` | `(data) => void` | ✅ | Callback avec les données saisies |
|
|
2065
2131
|
| `onSkip` | `() => void` | ✅ | Callback si l'utilisateur passe l'étape |
|
|
2066
2132
|
|
|
2067
2133
|
### Champs affichés
|
|
2068
2134
|
|
|
2069
|
-
La modal
|
|
2070
|
-
|
|
2071
|
-
- **
|
|
2072
|
-
- **
|
|
2135
|
+
La modal gère **deux modes** :
|
|
2136
|
+
|
|
2137
|
+
- **Mode `missing`** : affiche uniquement les champs manquants
|
|
2138
|
+
- **Nom complet** — si `user.name` est vide
|
|
2139
|
+
- **Photo de profil** — si `user.image_url` est vide (max 2 Mo, JPG/PNG)
|
|
2140
|
+
- **Numéro de téléphone** — si `user.phone` est vide
|
|
2141
|
+
- **Adresse email** — si `user.email` est vide
|
|
2142
|
+
- **Mode `edit`** : affiche les champs de base éditables même si le profil est déjà complet
|
|
2143
|
+
- Nom complet
|
|
2144
|
+
- Photo de profil
|
|
2145
|
+
- Numéro de téléphone en lecture seule avec bouton `Changer téléphone`
|
|
2146
|
+
- Adresse email en lecture seule avec bouton `Changer email`
|
|
2073
2147
|
|
|
2074
2148
|
### Callback `onComplete`
|
|
2075
2149
|
|
|
2076
2150
|
```ts
|
|
2077
2151
|
onComplete: (data: {
|
|
2152
|
+
name?: string; // Nom complet (si fourni ou modifié)
|
|
2078
2153
|
image_url?: string; // Base64 de la photo (si ajoutée)
|
|
2079
2154
|
ccphone?: string; // Indicatif (si téléphone ajouté)
|
|
2080
2155
|
phone?: string; // Numéro (si téléphone ajouté)
|
|
2081
|
-
email?: string; // Email (si renseigné
|
|
2156
|
+
email?: string; // Email (si renseigné)
|
|
2082
2157
|
}) => void;
|
|
2083
2158
|
```
|
|
2084
2159
|
|
|
2085
2160
|
### Condition de soumission
|
|
2086
2161
|
|
|
2087
2162
|
L'utilisateur **doit** :
|
|
2088
|
-
1.
|
|
2089
|
-
2. Fournir une photo
|
|
2090
|
-
3. Fournir un téléphone valide
|
|
2163
|
+
1. Fournir un nom si le nom est affiché
|
|
2164
|
+
2. Fournir une photo si elle est affichée
|
|
2165
|
+
3. Fournir un téléphone valide si le champ est affiché
|
|
2166
|
+
4. Fournir un email valide si le champ est affiché
|
|
2167
|
+
|
|
2168
|
+
Après connexion, la page autonome attend 5 minutes avant d'ouvrir cette modal si le profil est incomplet.
|
|
2169
|
+
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`).
|
|
2170
|
+
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.
|
|
2171
|
+
|
|
2172
|
+
### Mode automatique
|
|
2173
|
+
|
|
2174
|
+
Le mode automatique est piloté par `NativeSSOPage` :
|
|
2175
|
+
- la modal s'ouvre après le délai de 5 minutes si des infos sont manquantes,
|
|
2176
|
+
- elle reste sur un écran de chargement tant que l'hydratation IAM n'a pas renvoyé le profil,
|
|
2177
|
+
- elle affiche ensuite uniquement les champs manquants,
|
|
2178
|
+
- si l'utilisateur ferme la modal, le rappel est repoussé de 8 heures.
|
|
2091
2179
|
|
|
2092
|
-
|
|
2180
|
+
### Mode manuel depuis un SaaS
|
|
2181
|
+
|
|
2182
|
+
Quand vous ouvrez la modal depuis votre SaaS via un bouton "Infos profil" ou "Modifier mon profil", utilisez `variant="edit"`.
|
|
2183
|
+
|
|
2184
|
+
Exemple :
|
|
2185
|
+
|
|
2186
|
+
```tsx
|
|
2187
|
+
import { useState } from 'react';
|
|
2188
|
+
import { OnboardingModal } from '@ollaid/native-sso';
|
|
2189
|
+
|
|
2190
|
+
export function ProfileButton({ user }: { user: NativeUser }) {
|
|
2191
|
+
const [open, setOpen] = useState(false);
|
|
2192
|
+
|
|
2193
|
+
return (
|
|
2194
|
+
<>
|
|
2195
|
+
<button type="button" onClick={() => setOpen(true)}>
|
|
2196
|
+
Infos profil
|
|
2197
|
+
</button>
|
|
2198
|
+
|
|
2199
|
+
<OnboardingModal
|
|
2200
|
+
open={open}
|
|
2201
|
+
onOpenChange={setOpen}
|
|
2202
|
+
onDismiss={() => setOpen(false)}
|
|
2203
|
+
user={user}
|
|
2204
|
+
variant="edit"
|
|
2205
|
+
profileHydrating={false}
|
|
2206
|
+
onComplete={(data) => {
|
|
2207
|
+
// Mettre à jour votre cache local / localStorage avec data.user_infos
|
|
2208
|
+
setOpen(false);
|
|
2209
|
+
}}
|
|
2210
|
+
onSkip={() => setOpen(false)}
|
|
2211
|
+
/>
|
|
2212
|
+
</>
|
|
2213
|
+
);
|
|
2214
|
+
}
|
|
2215
|
+
```
|
|
2216
|
+
|
|
2217
|
+
Dans ce mode, le changement de téléphone/email ouvre un sous-flux OTP dans la même modal:
|
|
2218
|
+
1. saisie du nouveau téléphone/email,
|
|
2219
|
+
2. envoi du premier OTP sur le contact actuel,
|
|
2220
|
+
3. vérification du premier OTP,
|
|
2221
|
+
4. envoi d'un second OTP sur le nouveau contact,
|
|
2222
|
+
5. confirmation finale et mise à jour du profil.
|
|
2223
|
+
|
|
2224
|
+
Les écrans de changement restent en `static backdrop` côté package: un clic hors modal ne ferme pas le flux.
|
|
2093
2225
|
|
|
2094
2226
|
### Exemple
|
|
2095
2227
|
|
|
@@ -2100,6 +2232,7 @@ import { OnboardingModal } from '@ollaid/native-sso';
|
|
|
2100
2232
|
open={showOnboarding}
|
|
2101
2233
|
onOpenChange={setShowOnboarding}
|
|
2102
2234
|
user={currentUser}
|
|
2235
|
+
variant="edit"
|
|
2103
2236
|
onComplete={async (data) => {
|
|
2104
2237
|
// Envoyer les données au backend pour mise à jour
|
|
2105
2238
|
await api.updateProfile(data);
|
|
@@ -2248,6 +2381,9 @@ if (config('services.iam.debug')) {
|
|
|
2248
2381
|
- `getAuthToken` — Récupérer le token depuis localStorage
|
|
2249
2382
|
- `getAuthUser` — Récupérer l'utilisateur depuis localStorage
|
|
2250
2383
|
- `getAccountType` — Récupérer le type de compte depuis localStorage
|
|
2384
|
+
- `getDeviceId` — Récupérer/générer le `X-Device-Id` (persisté)
|
|
2385
|
+
- `getSessionUuid` — Récupérer/générer le `X-Session-UUID` (persisté)
|
|
2386
|
+
- `STORAGE_KEYS` — Constantes des clés localStorage utilisées par le package
|
|
2251
2387
|
|
|
2252
2388
|
### Types
|
|
2253
2389
|
- `UserInfos`, `NativeAuthState`, `NativeAuthStatus`, `NativeCredentials`, etc.
|
|
@@ -2352,3 +2488,16 @@ npm publish --access public # 3. Publier sur npm
|
|
|
2352
2488
|
## Licence
|
|
2353
2489
|
|
|
2354
2490
|
Propriétaire — Ollaid © 2026
|
|
2491
|
+
|
|
2492
|
+
---
|
|
2493
|
+
|
|
2494
|
+
## Webhooks & Health Check (Backend)
|
|
2495
|
+
|
|
2496
|
+
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.
|
|
2497
|
+
|
|
2498
|
+
Consultez le guide détaillé : [**WEBHOOKS_HEALTH.md**](./WEBHOOKS_HEALTH.md)
|
|
2499
|
+
|
|
2500
|
+
| Brique | Rôle |
|
|
2501
|
+
|--------|------|
|
|
2502
|
+
| **Health Check** | Permet à l'IAM de vérifier que votre SaaS est "Healthy" et bien configuré. |
|
|
2503
|
+
| **Webhooks** | Permet à l'IAM de notifier votre SaaS d'événements critiques (suspension, révocation). |
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AvatarCropModal — recadrage carré simple et déterministe
|
|
3
|
+
*
|
|
4
|
+
* Version réécrite from scratch pour éviter les états qui bloquent le bouton Valider.
|
|
5
|
+
*
|
|
6
|
+
* @version 2.5.0
|
|
7
|
+
*/
|
|
8
|
+
export interface AvatarCropModalProps {
|
|
9
|
+
open: boolean;
|
|
10
|
+
imageSrc: string | null;
|
|
11
|
+
onOpenChange: (open: boolean) => void;
|
|
12
|
+
onCancel: () => void;
|
|
13
|
+
onConfirm: (blob: Blob) => Promise<void> | void;
|
|
14
|
+
}
|
|
15
|
+
export declare function AvatarCropModal({ open, imageSrc, onOpenChange, onCancel, onConfirm }: AvatarCropModalProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export default AvatarCropModal;
|
|
@@ -2,11 +2,16 @@
|
|
|
2
2
|
* DebugPanel — Panneau de debug flottant pour @ollaid/native-sso
|
|
3
3
|
* Affiche l'historique des appels API en temps réel (style terminal)
|
|
4
4
|
* N'apparaît que quand debug=true
|
|
5
|
-
* @version 2.
|
|
5
|
+
* @version 2.5.0
|
|
6
6
|
*/
|
|
7
|
+
export type DebugOnboardingPreset = 'current' | 'photo' | 'phone' | 'email' | 'all';
|
|
7
8
|
interface DebugPanelProps {
|
|
8
9
|
saasApiUrl: string;
|
|
9
10
|
iamApiUrl: string;
|
|
11
|
+
onOpenLogin?: () => void;
|
|
12
|
+
onOpenSignup?: () => void;
|
|
13
|
+
onOpenOnboarding?: (preset: DebugOnboardingPreset) => void;
|
|
14
|
+
onResetProfilePrompt?: () => void;
|
|
10
15
|
}
|
|
11
|
-
export declare function DebugPanel({ saasApiUrl, iamApiUrl }: DebugPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare function DebugPanel({ saasApiUrl, iamApiUrl, onOpenLogin, onOpenSignup, onOpenOnboarding, onResetProfilePrompt }: DebugPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
12
17
|
export default DebugPanel;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Login Modal for @ollaid/native-sso
|
|
3
|
-
* Complete login flow aligned with
|
|
3
|
+
* Complete login flow aligned with Native SSO design
|
|
4
4
|
*
|
|
5
|
-
* @version 2.
|
|
5
|
+
* @version 2.5.0
|
|
6
6
|
*/
|
|
7
7
|
import type { UserInfos } from '../types/native';
|
|
8
8
|
export interface LoginModalProps {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* NativeSSOPage — Page autonome complète pour @ollaid/native-sso
|
|
3
|
-
* Design aligné sur
|
|
3
|
+
* Design aligné sur le parcours Native SSO (fond primary, card blanche, ShieldCheck branding)
|
|
4
4
|
*
|
|
5
|
-
* @version 2.
|
|
5
|
+
* @version 2.5.0
|
|
6
6
|
*/
|
|
7
7
|
import type { UserInfos } from '../types/native';
|
|
8
8
|
export interface NativeSSOPageProps {
|
|
@@ -11,9 +11,15 @@ export interface NativeSSOPageProps {
|
|
|
11
11
|
onLoginSuccess?: (token: string, user: UserInfos) => void;
|
|
12
12
|
onLogout?: () => void;
|
|
13
13
|
onOnboardingComplete?: (data: {
|
|
14
|
+
name?: string;
|
|
14
15
|
image_url?: string;
|
|
15
16
|
ccphone?: string;
|
|
16
17
|
phone?: string;
|
|
18
|
+
email?: string;
|
|
19
|
+
address?: string;
|
|
20
|
+
town?: string;
|
|
21
|
+
country?: string;
|
|
22
|
+
user_infos?: UserInfos;
|
|
17
23
|
}) => void;
|
|
18
24
|
accountType?: 'user' | 'client';
|
|
19
25
|
configPrefix?: string;
|
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OnboardingModal —
|
|
3
|
-
*
|
|
2
|
+
* OnboardingModal — Modal de profil unifiée
|
|
3
|
+
* Mode `missing` : champs absents uniquement
|
|
4
|
+
* Mode `edit` : édition complète du profil depuis le SaaS
|
|
4
5
|
*
|
|
5
|
-
* @version 2.
|
|
6
|
+
* @version 2.5.0
|
|
6
7
|
*/
|
|
7
|
-
import type { NativeUser } from '../types/native';
|
|
8
|
+
import type { NativeUser, UserInfos } from '../types/native';
|
|
8
9
|
export interface OnboardingModalProps {
|
|
9
10
|
open: boolean;
|
|
10
11
|
onOpenChange: (open: boolean) => void;
|
|
12
|
+
onDismiss: () => void;
|
|
11
13
|
user: NativeUser;
|
|
12
|
-
|
|
14
|
+
variant?: 'missing' | 'edit';
|
|
15
|
+
profileHydrating?: boolean;
|
|
13
16
|
onComplete: (data: {
|
|
17
|
+
name?: string;
|
|
14
18
|
image_url?: string;
|
|
15
19
|
ccphone?: string;
|
|
16
20
|
phone?: string;
|
|
17
21
|
email?: string;
|
|
22
|
+
address?: string;
|
|
23
|
+
town?: string;
|
|
24
|
+
country?: string;
|
|
25
|
+
user_infos?: UserInfos;
|
|
18
26
|
}) => void;
|
|
19
|
-
/** Called when user skips onboarding */
|
|
20
27
|
onSkip: () => void;
|
|
21
28
|
}
|
|
22
|
-
export declare function OnboardingModal({ open, onOpenChange, user, onComplete, onSkip }: OnboardingModalProps): import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
export declare function OnboardingModal({ open, onOpenChange, onDismiss, user, variant, profileHydrating, onComplete, onSkip }: OnboardingModalProps): import("react/jsx-runtime").JSX.Element;
|
|
23
30
|
export default OnboardingModal;
|
|
@@ -8,9 +8,10 @@ interface PhoneInputProps {
|
|
|
8
8
|
ccphone?: string;
|
|
9
9
|
onCcphoneChange?: (value: string) => void;
|
|
10
10
|
disabled?: boolean;
|
|
11
|
+
readOnly?: boolean;
|
|
11
12
|
error?: string;
|
|
12
13
|
placeholder?: string;
|
|
13
14
|
lockCcphone?: boolean;
|
|
14
15
|
}
|
|
15
|
-
export declare function PhoneInput({ value, onChange, ccphone, onCcphoneChange, disabled, error, placeholder, lockCcphone, }: PhoneInputProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare function PhoneInput({ value, onChange, ccphone, onCcphoneChange, disabled, readOnly, error, placeholder, lockCcphone, }: PhoneInputProps): import("react/jsx-runtime").JSX.Element;
|
|
16
17
|
export default PhoneInput;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Signup Modal for @ollaid/native-sso — Design aligned with web SSO
|
|
3
3
|
* Full signup flow: intro → account-type → info → OTP → password → confirm → success
|
|
4
4
|
*
|
|
5
|
-
* @version 2.
|
|
5
|
+
* @version 2.5.0
|
|
6
6
|
*/
|
|
7
7
|
import type { UserInfos } from '../types/native';
|
|
8
8
|
export interface SignupModalProps {
|
package/dist/components/ui.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Lightweight replacements for shadcn/ui components + inline SVG icons
|
|
4
4
|
* No external dependencies required
|
|
5
5
|
*
|
|
6
|
-
* @version 2.
|
|
6
|
+
* @version 2.5.0
|
|
7
7
|
*/
|
|
8
8
|
import React from 'react';
|
|
9
9
|
export declare function IconShieldCheck(props: React.SVGProps<SVGSVGElement>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -30,9 +30,11 @@ export declare function Dialog({ open, onOpenChange, children }: {
|
|
|
30
30
|
onOpenChange: (open: boolean) => void;
|
|
31
31
|
children: React.ReactNode;
|
|
32
32
|
}): import("react/jsx-runtime").JSX.Element | null;
|
|
33
|
-
export declare function DialogContent({ children, className }: {
|
|
33
|
+
export declare function DialogContent({ children, className, style, hideCloseButton }: {
|
|
34
34
|
children: React.ReactNode;
|
|
35
35
|
className?: string;
|
|
36
|
+
style?: React.CSSProperties;
|
|
37
|
+
hideCloseButton?: boolean;
|
|
36
38
|
}): import("react/jsx-runtime").JSX.Element;
|
|
37
39
|
export declare function DialogBody({ children, className, style }: {
|
|
38
40
|
children: React.ReactNode;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Hook d'authentification Native SSO v1.0
|
|
3
3
|
* Architecture Frontend-First avec appels directs à l'IAM
|
|
4
4
|
*
|
|
5
|
-
* @version 2.
|
|
5
|
+
* @version 2.5.0
|
|
6
6
|
*/
|
|
7
7
|
import type { NativeAuthStatus, NativeExchangeResponse, AccountType } from '../types/native';
|
|
8
8
|
export interface UseNativeAuthOptions {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - Si 401 → révoque l'IAM (POST /iam/disconnect) + nettoie le frontend
|
|
11
11
|
* - Ne déconnecte PAS si offline ou serveur inaccessible
|
|
12
12
|
*
|
|
13
|
-
* @version 2.
|
|
13
|
+
* @version 2.5.0
|
|
14
14
|
*/
|
|
15
15
|
import type { UserInfos } from '../types/native';
|
|
16
16
|
export interface UseTokenHealthCheckOptions {
|
|
@@ -22,6 +22,16 @@ export interface UseTokenHealthCheckOptions {
|
|
|
22
22
|
iamApiUrl: string;
|
|
23
23
|
/** Called when the backend explicitly invalidates the token (401) */
|
|
24
24
|
onTokenInvalid: () => void;
|
|
25
|
+
/**
|
|
26
|
+
* Optional handler for 401. If provided, it can attempt a refresh and decide
|
|
27
|
+
* whether the session was recovered.
|
|
28
|
+
*
|
|
29
|
+
* Return values:
|
|
30
|
+
* - 'recovered': session is valid again (do NOT call onTokenInvalid)
|
|
31
|
+
* - 'invalid': token is explicitly invalid (call onTokenInvalid)
|
|
32
|
+
* - 'noop': fall back to default behavior
|
|
33
|
+
*/
|
|
34
|
+
onUnauthorized?: () => Promise<'recovered' | 'invalid' | 'noop'>;
|
|
25
35
|
/** Called when fresh user_infos are received */
|
|
26
36
|
onUserUpdated?: (userInfos: UserInfos) => void;
|
|
27
37
|
/** Debug mode */
|