@ollaid/native-sso 1.0.0 → 1.0.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 +29 -8
- package/dist/components/LoginModal.d.ts +3 -2
- package/dist/components/NativeSSOPage.d.ts +3 -3
- package/dist/components/PasswordRecoveryModal.d.ts +1 -2
- package/dist/components/SignupModal.d.ts +3 -2
- package/dist/hooks/useMobilePassword.d.ts +0 -1
- package/dist/hooks/useMobileRegistration.d.ts +0 -1
- package/dist/hooks/useNativeAuth.d.ts +66 -6
- package/dist/index.cjs +91 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +91 -30
- package/dist/index.js.map +1 -1
- package/dist/services/api.d.ts +1 -0
- package/dist/services/nativeAuth.d.ts +4 -0
- package/dist/types/native.d.ts +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -60,6 +60,7 @@ function App() {
|
|
|
60
60
|
<NativeSSOPage
|
|
61
61
|
saasApiUrl="https://mon-saas.com/api"
|
|
62
62
|
iamApiUrl="https://identityam.ollaid.com/api"
|
|
63
|
+
accountType="user"
|
|
63
64
|
onLoginSuccess={(token, user) => {
|
|
64
65
|
console.log('Connecté !', user.name);
|
|
65
66
|
navigate('/dashboard');
|
|
@@ -95,15 +96,17 @@ La page `/auth/sso` gère automatiquement :
|
|
|
95
96
|
|------|------|--------|-------------|
|
|
96
97
|
| `saasApiUrl` | `string` | ✅ | URL du backend SaaS (ex: `https://mon-saas.com/api`) |
|
|
97
98
|
| `iamApiUrl` | `string` | ✅ | URL du backend IAM (ex: `https://identityam.ollaid.com/api`) |
|
|
99
|
+
| `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. |
|
|
98
100
|
| `onLoginSuccess` | `(token: string, user: UserInfos) => void` | ❌ | Callback après connexion réussie |
|
|
99
101
|
| `onLogout` | `() => void` | ❌ | Callback après déconnexion |
|
|
100
|
-
| `debug` | `boolean` | ❌ | Active les logs console |
|
|
101
102
|
| `title` | `string` | ❌ | Titre personnalisé (défaut: "Un compte, plusieurs accès") |
|
|
102
103
|
| `description` | `string` | ❌ | Description personnalisée |
|
|
103
104
|
| `logoUrl` | `string` | ❌ | URL du logo (remplace le slider) |
|
|
104
105
|
| `hideFooter` | `boolean` | ❌ | Masquer "Propulsé par iam.ollaid.com" |
|
|
105
106
|
| `onOnboardingComplete` | `(data: { image_url?: string; ccphone?: string; phone?: string }) => void` | ❌ | Callback après complétion de l'onboarding |
|
|
106
107
|
|
|
108
|
+
> **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.
|
|
109
|
+
|
|
107
110
|
---
|
|
108
111
|
|
|
109
112
|
## Usage avancé (composants individuels)
|
|
@@ -227,6 +230,11 @@ $secretKey = $payload['secret_key'];
|
|
|
227
230
|
|
|
228
231
|
### `POST /api/native/exchange`
|
|
229
232
|
|
|
233
|
+
> ⚠️ **IMPORTANT — Route PUBLIQUE obligatoire**
|
|
234
|
+
> Cette route **NE DOIT PAS** être derrière le middleware `auth:sanctum`.
|
|
235
|
+
> Si vous la placez dans un groupe `Route::middleware('auth:sanctum')`, le frontend recevra une **erreur 401 Unauthenticated** systématique car l'utilisateur n'a pas encore de token Sanctum à ce stade du flux.
|
|
236
|
+
> Assurez-vous que `/api/native/config` et `/api/native/exchange` sont **en dehors** de tout groupe authentifié.
|
|
237
|
+
|
|
230
238
|
Échange le `callback_token` reçu de l'IAM contre un token Sanctum local.
|
|
231
239
|
|
|
232
240
|
**Headers requis :** `Content-Type: application/json`
|
|
@@ -665,14 +673,19 @@ class NativeAuthController extends Controller
|
|
|
665
673
|
|
|
666
674
|
### Routes `routes/api.php`
|
|
667
675
|
|
|
676
|
+
> ⚠️ **Attention au placement des routes !**
|
|
677
|
+
> `config` et `exchange` doivent être **publiques** (pas de middleware auth).
|
|
678
|
+
> `check-token` et `logout` doivent être **protégées** par `auth:sanctum`.
|
|
679
|
+
> Si `exchange` est derrière `auth:sanctum`, le frontend recevra un **401 Unauthenticated**.
|
|
680
|
+
|
|
668
681
|
```php
|
|
669
682
|
use App\Http\Controllers\Api\NativeAuthController;
|
|
670
683
|
|
|
671
|
-
// Routes
|
|
684
|
+
// ✅ Routes PUBLIQUES (pas d'auth requise — l'utilisateur n'a pas encore de token)
|
|
672
685
|
Route::get('/native/config', [NativeAuthController::class, 'config']);
|
|
673
686
|
Route::post('/native/exchange', [NativeAuthController::class, 'exchange']);
|
|
674
687
|
|
|
675
|
-
// Routes
|
|
688
|
+
// 🔒 Routes PROTÉGÉES (auth Sanctum requise — l'utilisateur a un token)
|
|
676
689
|
Route::middleware('auth:sanctum')->group(function () {
|
|
677
690
|
Route::post('/native/check-token', [NativeAuthController::class, 'checkToken']);
|
|
678
691
|
Route::post('/native/logout', [NativeAuthController::class, 'logout']);
|
|
@@ -1370,20 +1383,27 @@ Toutes les APIs IAM retournent le même objet `user_infos` avec exactement **9 c
|
|
|
1370
1383
|
|
|
1371
1384
|
## Session & localStorage
|
|
1372
1385
|
|
|
1373
|
-
Le package utilise **
|
|
1386
|
+
Le package utilise **5 clés** dans `localStorage` pour persister la session :
|
|
1374
1387
|
|
|
1375
1388
|
| Clé | Contenu | Source | Valeurs possibles |
|
|
1376
1389
|
|-----|---------|--------|-------------------|
|
|
1377
1390
|
| `token` | Token Sanctum (bearer) | Réponse de `/api/native/exchange` | Chaîne `"1\|abc123..."` |
|
|
1378
1391
|
| `auth_token` | Copie du token (compatibilité) | Même source que `token` | Idem |
|
|
1379
|
-
| `user` | Objet
|
|
1392
|
+
| `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":"...",...}` |
|
|
1380
1393
|
| `account_type` | Type de compte | Déterminé lors du `exchange` selon le mode d'inscription | `"user"` (défaut) ou `"client"` (inscription phone-only) |
|
|
1394
|
+
| `alias_reference` | Référence alias utilisée lors de la connexion | Réponse de `/api/native/exchange` (champ `user.alias_reference`) | `"ALI-XXXXXXXX"` |
|
|
1395
|
+
|
|
1396
|
+
> **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`.
|
|
1397
|
+
|
|
1398
|
+
#### Champs enrichis dans l'objet `user`
|
|
1381
1399
|
|
|
1382
|
-
|
|
1400
|
+
L'objet `user` stocké en localStorage contient deux champs ajoutés automatiquement par le package :
|
|
1401
|
+
- `iam_reference` : la référence IAM de l'utilisateur (`USR-XXXXXXXX`), extraite de `user.reference`
|
|
1402
|
+
- `alias_reference` : la référence de l'alias utilisé lors de la connexion (`ALI-XXXXXXXX`), extraite de `user.alias_reference`
|
|
1383
1403
|
|
|
1384
1404
|
### Nettoyage
|
|
1385
1405
|
|
|
1386
|
-
Lors du `logout()`, les
|
|
1406
|
+
Lors du `logout()`, les 5 clés sont supprimées via `clearAuthToken()`.
|
|
1387
1407
|
|
|
1388
1408
|
### Accès programmatique
|
|
1389
1409
|
|
|
@@ -1391,8 +1411,9 @@ Lors du `logout()`, les 4 clés sont supprimées via `clearAuthToken()`.
|
|
|
1391
1411
|
import { getAuthToken, getAuthUser, getAccountType } from '@ollaid/native-sso';
|
|
1392
1412
|
|
|
1393
1413
|
const token = getAuthToken(); // string | null
|
|
1394
|
-
const user = getAuthUser<UserInfos>(); // UserInfos | null
|
|
1414
|
+
const user = getAuthUser<UserInfos>(); // UserInfos | null — contient iam_reference et alias_reference
|
|
1395
1415
|
const type = getAccountType(); // string | null
|
|
1416
|
+
const aliasRef = localStorage.getItem('alias_reference'); // string | null
|
|
1396
1417
|
```
|
|
1397
1418
|
|
|
1398
1419
|
---
|
|
@@ -14,7 +14,8 @@ export interface LoginModalProps {
|
|
|
14
14
|
iamApiUrl: string;
|
|
15
15
|
loading?: boolean;
|
|
16
16
|
showSwitchToSignup?: boolean;
|
|
17
|
-
|
|
17
|
+
/** Type de compte par défaut à persister dans localStorage */
|
|
18
|
+
defaultAccountType?: 'user' | 'client';
|
|
18
19
|
}
|
|
19
|
-
export declare function LoginModal({ open, onOpenChange, onSwitchToSignup, onLoginSuccess, saasApiUrl, iamApiUrl, loading, showSwitchToSignup,
|
|
20
|
+
export declare function LoginModal({ open, onOpenChange, onSwitchToSignup, onLoginSuccess, saasApiUrl, iamApiUrl, loading, showSwitchToSignup, defaultAccountType, }: LoginModalProps): import("react/jsx-runtime").JSX.Element;
|
|
20
21
|
export default LoginModal;
|
|
@@ -20,8 +20,8 @@ export interface NativeSSOPageProps {
|
|
|
20
20
|
ccphone?: string;
|
|
21
21
|
phone?: string;
|
|
22
22
|
}) => void;
|
|
23
|
-
/**
|
|
24
|
-
|
|
23
|
+
/** Type de compte à persister dans localStorage (défaut: 'user') */
|
|
24
|
+
accountType?: 'user' | 'client';
|
|
25
25
|
/** Titre personnalisé */
|
|
26
26
|
title?: string;
|
|
27
27
|
/** Description personnalisée */
|
|
@@ -31,5 +31,5 @@ export interface NativeSSOPageProps {
|
|
|
31
31
|
/** Masquer le footer "Propulsé par" */
|
|
32
32
|
hideFooter?: boolean;
|
|
33
33
|
}
|
|
34
|
-
export declare function NativeSSOPage({ saasApiUrl, iamApiUrl, onLoginSuccess, onLogout, onOnboardingComplete,
|
|
34
|
+
export declare function NativeSSOPage({ saasApiUrl, iamApiUrl, onLoginSuccess, onLogout, onOnboardingComplete, accountType, title, description, logoUrl, hideFooter, }: NativeSSOPageProps): import("react/jsx-runtime").JSX.Element;
|
|
35
35
|
export default NativeSSOPage;
|
|
@@ -11,7 +11,6 @@ export interface PasswordRecoveryModalProps {
|
|
|
11
11
|
onSuccess: () => void;
|
|
12
12
|
saasApiUrl: string;
|
|
13
13
|
iamApiUrl: string;
|
|
14
|
-
debug?: boolean;
|
|
15
14
|
}
|
|
16
|
-
export declare function PasswordRecoveryModal({ open, onOpenChange, onSuccess, saasApiUrl, iamApiUrl
|
|
15
|
+
export declare function PasswordRecoveryModal({ open, onOpenChange, onSuccess, saasApiUrl, iamApiUrl }: PasswordRecoveryModalProps): import("react/jsx-runtime").JSX.Element;
|
|
17
16
|
export default PasswordRecoveryModal;
|
|
@@ -12,7 +12,8 @@ export interface SignupModalProps {
|
|
|
12
12
|
onSignupSuccess: (token: string, user: UserInfos) => void;
|
|
13
13
|
saasApiUrl: string;
|
|
14
14
|
iamApiUrl: string;
|
|
15
|
-
|
|
15
|
+
/** Type de compte par défaut à persister dans localStorage */
|
|
16
|
+
defaultAccountType?: 'user' | 'client';
|
|
16
17
|
}
|
|
17
|
-
export declare function SignupModal({ open, onOpenChange, onSwitchToLogin, onSignupSuccess, saasApiUrl, iamApiUrl,
|
|
18
|
+
export declare function SignupModal({ open, onOpenChange, onSwitchToLogin, onSignupSuccess, saasApiUrl, iamApiUrl, defaultAccountType }: SignupModalProps): import("react/jsx-runtime").JSX.Element;
|
|
18
19
|
export default SignupModal;
|
|
@@ -19,7 +19,6 @@ interface RegistrationConflict {
|
|
|
19
19
|
export interface UseMobileRegistrationOptions {
|
|
20
20
|
saasApiUrl: string;
|
|
21
21
|
iamApiUrl: string;
|
|
22
|
-
debug?: boolean;
|
|
23
22
|
}
|
|
24
23
|
export declare function useMobileRegistration(options?: UseMobileRegistrationOptions): {
|
|
25
24
|
processToken: string | null;
|
|
@@ -12,8 +12,8 @@ export interface UseNativeAuthOptions {
|
|
|
12
12
|
iamApiUrl: string;
|
|
13
13
|
/** Charger les credentials automatiquement au montage */
|
|
14
14
|
autoLoadCredentials?: boolean;
|
|
15
|
-
/**
|
|
16
|
-
|
|
15
|
+
/** Type de compte par défaut à persister (défaut: 'user') */
|
|
16
|
+
defaultAccountType?: 'user' | 'client';
|
|
17
17
|
}
|
|
18
18
|
export declare function useNativeAuth(options: UseNativeAuthOptions): {
|
|
19
19
|
credentialsLoaded: boolean;
|
|
@@ -117,7 +117,22 @@ export declare function useNativeAuth(options: UseNativeAuthOptions): {
|
|
|
117
117
|
error_type?: undefined;
|
|
118
118
|
} | {
|
|
119
119
|
success: boolean;
|
|
120
|
-
user:
|
|
120
|
+
user: {
|
|
121
|
+
iam_reference: string;
|
|
122
|
+
alias_reference: any;
|
|
123
|
+
id?: number;
|
|
124
|
+
reference: string;
|
|
125
|
+
name: string;
|
|
126
|
+
email?: string | null;
|
|
127
|
+
phone?: string;
|
|
128
|
+
ccphone?: string;
|
|
129
|
+
image_url?: string;
|
|
130
|
+
account_type?: "user" | "client";
|
|
131
|
+
town?: string;
|
|
132
|
+
country?: string;
|
|
133
|
+
address?: string;
|
|
134
|
+
auth_2fa?: boolean;
|
|
135
|
+
};
|
|
121
136
|
callback_token: string;
|
|
122
137
|
error?: undefined;
|
|
123
138
|
requires_2fa?: undefined;
|
|
@@ -157,7 +172,22 @@ export declare function useNativeAuth(options: UseNativeAuthOptions): {
|
|
|
157
172
|
error_type?: undefined;
|
|
158
173
|
} | {
|
|
159
174
|
success: boolean;
|
|
160
|
-
user:
|
|
175
|
+
user: {
|
|
176
|
+
iam_reference: string;
|
|
177
|
+
alias_reference: any;
|
|
178
|
+
id?: number;
|
|
179
|
+
reference: string;
|
|
180
|
+
name: string;
|
|
181
|
+
email?: string | null;
|
|
182
|
+
phone?: string;
|
|
183
|
+
ccphone?: string;
|
|
184
|
+
image_url?: string;
|
|
185
|
+
account_type?: "user" | "client";
|
|
186
|
+
town?: string;
|
|
187
|
+
country?: string;
|
|
188
|
+
address?: string;
|
|
189
|
+
auth_2fa?: boolean;
|
|
190
|
+
};
|
|
161
191
|
callback_token: string;
|
|
162
192
|
error?: undefined;
|
|
163
193
|
status?: undefined;
|
|
@@ -184,7 +214,22 @@ export declare function useNativeAuth(options: UseNativeAuthOptions): {
|
|
|
184
214
|
error_type?: undefined;
|
|
185
215
|
} | {
|
|
186
216
|
success: boolean;
|
|
187
|
-
user:
|
|
217
|
+
user: {
|
|
218
|
+
iam_reference: string;
|
|
219
|
+
alias_reference: any;
|
|
220
|
+
id?: number;
|
|
221
|
+
reference: string;
|
|
222
|
+
name: string;
|
|
223
|
+
email?: string | null;
|
|
224
|
+
phone?: string;
|
|
225
|
+
ccphone?: string;
|
|
226
|
+
image_url?: string;
|
|
227
|
+
account_type?: "user" | "client";
|
|
228
|
+
town?: string;
|
|
229
|
+
country?: string;
|
|
230
|
+
address?: string;
|
|
231
|
+
auth_2fa?: boolean;
|
|
232
|
+
};
|
|
188
233
|
error?: undefined;
|
|
189
234
|
error_type?: undefined;
|
|
190
235
|
} | {
|
|
@@ -201,7 +246,22 @@ export declare function useNativeAuth(options: UseNativeAuthOptions): {
|
|
|
201
246
|
error_type?: undefined;
|
|
202
247
|
} | {
|
|
203
248
|
success: boolean;
|
|
204
|
-
user:
|
|
249
|
+
user: {
|
|
250
|
+
iam_reference: string;
|
|
251
|
+
alias_reference: any;
|
|
252
|
+
id?: number;
|
|
253
|
+
reference: string;
|
|
254
|
+
name: string;
|
|
255
|
+
email?: string | null;
|
|
256
|
+
phone?: string;
|
|
257
|
+
ccphone?: string;
|
|
258
|
+
image_url?: string;
|
|
259
|
+
account_type?: "user" | "client";
|
|
260
|
+
town?: string;
|
|
261
|
+
country?: string;
|
|
262
|
+
address?: string;
|
|
263
|
+
auth_2fa?: boolean;
|
|
264
|
+
};
|
|
205
265
|
callback_token: string;
|
|
206
266
|
error?: undefined;
|
|
207
267
|
error_type?: undefined;
|
package/dist/index.cjs
CHANGED
|
@@ -481,7 +481,8 @@ const STORAGE = {
|
|
|
481
481
|
AUTH_TOKEN: "auth_token",
|
|
482
482
|
TOKEN: "token",
|
|
483
483
|
USER: "user",
|
|
484
|
-
ACCOUNT_TYPE: "account_type"
|
|
484
|
+
ACCOUNT_TYPE: "account_type",
|
|
485
|
+
ALIAS_REFERENCE: "alias_reference"
|
|
485
486
|
};
|
|
486
487
|
const setAuthToken = (token) => {
|
|
487
488
|
if (typeof localStorage !== "undefined") {
|
|
@@ -499,6 +500,7 @@ const clearAuthToken = () => {
|
|
|
499
500
|
localStorage.removeItem(STORAGE.TOKEN);
|
|
500
501
|
localStorage.removeItem(STORAGE.USER);
|
|
501
502
|
localStorage.removeItem(STORAGE.ACCOUNT_TYPE);
|
|
503
|
+
localStorage.removeItem(STORAGE.ALIAS_REFERENCE);
|
|
502
504
|
}
|
|
503
505
|
};
|
|
504
506
|
const setAuthUser = (user) => {
|
|
@@ -587,6 +589,10 @@ function getHeaders(token) {
|
|
|
587
589
|
return headers;
|
|
588
590
|
}
|
|
589
591
|
let credentials = null;
|
|
592
|
+
let credentialsLoadedAt = 0;
|
|
593
|
+
let credentialsTtl = 300;
|
|
594
|
+
const DEFAULT_TTL = 300;
|
|
595
|
+
const REFRESH_MARGIN = 30;
|
|
590
596
|
const nativeAuthService = {
|
|
591
597
|
hasCredentials() {
|
|
592
598
|
return credentials !== null;
|
|
@@ -594,6 +600,26 @@ const nativeAuthService = {
|
|
|
594
600
|
getCredentials() {
|
|
595
601
|
return credentials ? { ...credentials } : null;
|
|
596
602
|
},
|
|
603
|
+
getCredentialsTtl() {
|
|
604
|
+
return credentialsTtl;
|
|
605
|
+
},
|
|
606
|
+
getCredentialsAge() {
|
|
607
|
+
if (!credentialsLoadedAt) return Infinity;
|
|
608
|
+
return (Date.now() - credentialsLoadedAt) / 1e3;
|
|
609
|
+
},
|
|
610
|
+
areCredentialsExpiringSoon() {
|
|
611
|
+
if (!credentials || !credentialsLoadedAt) return true;
|
|
612
|
+
const age = this.getCredentialsAge();
|
|
613
|
+
return age >= credentialsTtl - REFRESH_MARGIN;
|
|
614
|
+
},
|
|
615
|
+
async ensureFreshCredentials() {
|
|
616
|
+
if (!credentials || this.areCredentialsExpiringSoon()) {
|
|
617
|
+
if (isDebugMode()) {
|
|
618
|
+
console.log("🔄 [Native] Credentials expirés ou proches — auto-refresh");
|
|
619
|
+
}
|
|
620
|
+
await this.loadCredentials();
|
|
621
|
+
}
|
|
622
|
+
},
|
|
597
623
|
async loadCredentials() {
|
|
598
624
|
const config2 = getNativeAuthConfig();
|
|
599
625
|
if (!config2.saasApiUrl) {
|
|
@@ -614,6 +640,8 @@ const nativeAuthService = {
|
|
|
614
640
|
appKey: response.app_key,
|
|
615
641
|
encryptedCredentials: response.encrypted_credentials
|
|
616
642
|
};
|
|
643
|
+
credentialsLoadedAt = Date.now();
|
|
644
|
+
credentialsTtl = response.credentials_ttl || DEFAULT_TTL;
|
|
617
645
|
if (typeof response.debug === "boolean") {
|
|
618
646
|
const currentConfig = getNativeAuthConfig();
|
|
619
647
|
if (currentConfig.debug !== true) {
|
|
@@ -621,11 +649,12 @@ const nativeAuthService = {
|
|
|
621
649
|
}
|
|
622
650
|
}
|
|
623
651
|
if (isDebugMode()) {
|
|
624
|
-
console.log("✅ [SaaS] Credentials chargés (debug:", response.debug ?? "non défini", ")");
|
|
652
|
+
console.log("✅ [SaaS] Credentials chargés (ttl:", credentialsTtl, "s, debug:", response.debug ?? "non défini", ")");
|
|
625
653
|
}
|
|
626
654
|
return credentials;
|
|
627
655
|
},
|
|
628
656
|
async encrypt(type, data) {
|
|
657
|
+
await this.ensureFreshCredentials();
|
|
629
658
|
if (!credentials) {
|
|
630
659
|
throw new ApiError("Credentials non chargés. Appelez loadCredentials() d'abord.", "auth");
|
|
631
660
|
}
|
|
@@ -833,15 +862,18 @@ const nativeAuthService = {
|
|
|
833
862
|
);
|
|
834
863
|
clearAuthToken();
|
|
835
864
|
credentials = null;
|
|
865
|
+
credentialsLoadedAt = 0;
|
|
836
866
|
return response;
|
|
837
867
|
} catch {
|
|
838
868
|
clearAuthToken();
|
|
839
869
|
credentials = null;
|
|
870
|
+
credentialsLoadedAt = 0;
|
|
840
871
|
return { success: true };
|
|
841
872
|
}
|
|
842
873
|
},
|
|
843
874
|
clearCredentials() {
|
|
844
875
|
credentials = null;
|
|
876
|
+
credentialsLoadedAt = 0;
|
|
845
877
|
},
|
|
846
878
|
// ============================================
|
|
847
879
|
// High-level methods
|
|
@@ -1024,14 +1056,14 @@ function getErrorMessage$2(err, context) {
|
|
|
1024
1056
|
return { message: `Erreur lors de ${context}`, type: "unknown" };
|
|
1025
1057
|
}
|
|
1026
1058
|
function useMobilePassword(options) {
|
|
1027
|
-
const { saasApiUrl, iamApiUrl, autoLoadCredentials = true
|
|
1059
|
+
const { saasApiUrl, iamApiUrl, autoLoadCredentials = true } = options;
|
|
1028
1060
|
const configuredRef = react.useRef(false);
|
|
1029
1061
|
react.useEffect(() => {
|
|
1030
1062
|
if (!configuredRef.current) {
|
|
1031
|
-
setNativeAuthConfig({ saasApiUrl, iamApiUrl
|
|
1063
|
+
setNativeAuthConfig({ saasApiUrl, iamApiUrl });
|
|
1032
1064
|
configuredRef.current = true;
|
|
1033
1065
|
}
|
|
1034
|
-
}, [saasApiUrl, iamApiUrl
|
|
1066
|
+
}, [saasApiUrl, iamApiUrl]);
|
|
1035
1067
|
const [state, setState] = react.useState({
|
|
1036
1068
|
credentialsLoaded: false,
|
|
1037
1069
|
processToken: null,
|
|
@@ -1267,7 +1299,7 @@ const backBtnStyle$2 = {
|
|
|
1267
1299
|
color: C$3.gray700,
|
|
1268
1300
|
zIndex: 10
|
|
1269
1301
|
};
|
|
1270
|
-
function PasswordRecoveryModal({ open, onOpenChange, onSuccess, saasApiUrl, iamApiUrl
|
|
1302
|
+
function PasswordRecoveryModal({ open, onOpenChange, onSuccess, saasApiUrl, iamApiUrl }) {
|
|
1271
1303
|
const {
|
|
1272
1304
|
status,
|
|
1273
1305
|
loading: pwLoading,
|
|
@@ -1282,7 +1314,7 @@ function PasswordRecoveryModal({ open, onOpenChange, onSuccess, saasApiUrl, iamA
|
|
|
1282
1314
|
resendOtp,
|
|
1283
1315
|
reset: resetHook,
|
|
1284
1316
|
clearError
|
|
1285
|
-
} = useMobilePassword({ saasApiUrl, iamApiUrl
|
|
1317
|
+
} = useMobilePassword({ saasApiUrl, iamApiUrl });
|
|
1286
1318
|
const [step, setStep] = react.useState("email");
|
|
1287
1319
|
const [email, setEmail] = react.useState("");
|
|
1288
1320
|
const [otp, setOtp] = react.useState("");
|
|
@@ -1569,11 +1601,22 @@ function useTokenHealthCheck(options) {
|
|
|
1569
1601
|
}, [enabled, saasApiUrl, performCheck, debug]);
|
|
1570
1602
|
}
|
|
1571
1603
|
function saveSession(exchangeResult, accountType) {
|
|
1604
|
+
var _a, _b;
|
|
1572
1605
|
const sanctumToken = exchangeResult.auth_token || exchangeResult.token;
|
|
1573
1606
|
localStorage.setItem(STORAGE.AUTH_TOKEN, sanctumToken);
|
|
1574
1607
|
localStorage.setItem(STORAGE.TOKEN, sanctumToken);
|
|
1575
|
-
const
|
|
1576
|
-
const
|
|
1608
|
+
const baseUser = exchangeResult.user_infos ? { ...exchangeResult.user, ...exchangeResult.user_infos } : exchangeResult.user;
|
|
1609
|
+
const aliasRef = ((_a = exchangeResult.user) == null ? void 0 : _a.alias_reference) || exchangeResult.alias_reference || "";
|
|
1610
|
+
const iamRef = ((_b = exchangeResult.user) == null ? void 0 : _b.reference) || "";
|
|
1611
|
+
const userToStore = {
|
|
1612
|
+
...baseUser,
|
|
1613
|
+
iam_reference: iamRef,
|
|
1614
|
+
alias_reference: aliasRef
|
|
1615
|
+
};
|
|
1616
|
+
if (aliasRef) {
|
|
1617
|
+
localStorage.setItem(STORAGE.ALIAS_REFERENCE, aliasRef);
|
|
1618
|
+
}
|
|
1619
|
+
const acctType = typeof accountType === "string" ? accountType : "user";
|
|
1577
1620
|
localStorage.setItem(STORAGE.USER, JSON.stringify(userToStore));
|
|
1578
1621
|
localStorage.setItem(STORAGE.ACCOUNT_TYPE, acctType);
|
|
1579
1622
|
return { token: sanctumToken, user: userToStore };
|
|
@@ -1583,6 +1626,7 @@ function clearSession() {
|
|
|
1583
1626
|
localStorage.removeItem(STORAGE.TOKEN);
|
|
1584
1627
|
localStorage.removeItem(STORAGE.USER);
|
|
1585
1628
|
localStorage.removeItem(STORAGE.ACCOUNT_TYPE);
|
|
1629
|
+
localStorage.removeItem(STORAGE.ALIAS_REFERENCE);
|
|
1586
1630
|
}
|
|
1587
1631
|
function getErrorMessage$1(err, context) {
|
|
1588
1632
|
if (err instanceof Error) {
|
|
@@ -1597,14 +1641,15 @@ function getErrorMessage$1(err, context) {
|
|
|
1597
1641
|
return { message: `Erreur lors de ${context}`, type: "unknown" };
|
|
1598
1642
|
}
|
|
1599
1643
|
function useNativeAuth(options) {
|
|
1600
|
-
const { saasApiUrl, iamApiUrl, autoLoadCredentials = true,
|
|
1644
|
+
const { saasApiUrl, iamApiUrl, autoLoadCredentials = true, defaultAccountType = "user" } = options;
|
|
1601
1645
|
const configuredRef = react.useRef(false);
|
|
1646
|
+
const debug = isDebugMode();
|
|
1602
1647
|
react.useEffect(() => {
|
|
1603
1648
|
if (!configuredRef.current) {
|
|
1604
|
-
setNativeAuthConfig({ saasApiUrl, iamApiUrl
|
|
1649
|
+
setNativeAuthConfig({ saasApiUrl, iamApiUrl });
|
|
1605
1650
|
configuredRef.current = true;
|
|
1606
1651
|
}
|
|
1607
|
-
}, [saasApiUrl, iamApiUrl
|
|
1652
|
+
}, [saasApiUrl, iamApiUrl]);
|
|
1608
1653
|
const [state, setState] = react.useState({
|
|
1609
1654
|
credentialsLoaded: false,
|
|
1610
1655
|
processToken: null,
|
|
@@ -1676,6 +1721,23 @@ function useNativeAuth(options) {
|
|
|
1676
1721
|
loadCredentials();
|
|
1677
1722
|
}
|
|
1678
1723
|
}, [autoLoadCredentials, state.credentialsLoaded, state.user]);
|
|
1724
|
+
react.useEffect(() => {
|
|
1725
|
+
if (state.status === "completed" || !state.credentialsLoaded) return;
|
|
1726
|
+
const ttl = nativeAuthService.getCredentialsTtl();
|
|
1727
|
+
const refreshInterval = Math.max((ttl - 30) * 1e3, 3e4);
|
|
1728
|
+
if (debug) {
|
|
1729
|
+
console.log(`🔄 [Native] Auto-refresh credentials every ${refreshInterval / 1e3}s (TTL: ${ttl}s)`);
|
|
1730
|
+
}
|
|
1731
|
+
const timer = setInterval(async () => {
|
|
1732
|
+
try {
|
|
1733
|
+
await nativeAuthService.loadCredentials();
|
|
1734
|
+
if (debug) console.log("✅ [Native] Credentials auto-refreshed");
|
|
1735
|
+
} catch (err) {
|
|
1736
|
+
if (debug) console.warn("⚠️ [Native] Auto-refresh failed:", err);
|
|
1737
|
+
}
|
|
1738
|
+
}, refreshInterval);
|
|
1739
|
+
return () => clearInterval(timer);
|
|
1740
|
+
}, [state.status, state.credentialsLoaded, debug]);
|
|
1679
1741
|
const loadCredentials = react.useCallback(async () => {
|
|
1680
1742
|
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
1681
1743
|
try {
|
|
@@ -1903,7 +1965,7 @@ function useNativeAuth(options) {
|
|
|
1903
1965
|
if (response.success && response.callback_token) {
|
|
1904
1966
|
const exchangeResult = await nativeAuthService.exchange(response.callback_token);
|
|
1905
1967
|
if (exchangeResult.success) {
|
|
1906
|
-
const { user: savedUser } = saveSession(exchangeResult,
|
|
1968
|
+
const { user: savedUser } = saveSession(exchangeResult, defaultAccountType);
|
|
1907
1969
|
setState((prev) => ({
|
|
1908
1970
|
...prev,
|
|
1909
1971
|
status: "completed",
|
|
@@ -1964,7 +2026,7 @@ function useNativeAuth(options) {
|
|
|
1964
2026
|
if (response.success && response.callback_token) {
|
|
1965
2027
|
const exchangeResult = await nativeAuthService.exchange(response.callback_token);
|
|
1966
2028
|
if (exchangeResult.success) {
|
|
1967
|
-
const { user: savedUser } = saveSession(exchangeResult,
|
|
2029
|
+
const { user: savedUser } = saveSession(exchangeResult, defaultAccountType);
|
|
1968
2030
|
setState((prev) => ({
|
|
1969
2031
|
...prev,
|
|
1970
2032
|
status: "completed",
|
|
@@ -2025,7 +2087,7 @@ function useNativeAuth(options) {
|
|
|
2025
2087
|
if (response.success && response.callback_token) {
|
|
2026
2088
|
const exchangeResult = await nativeAuthService.exchange(response.callback_token);
|
|
2027
2089
|
if (exchangeResult.success) {
|
|
2028
|
-
const { user: savedUser } = saveSession(exchangeResult,
|
|
2090
|
+
const { user: savedUser } = saveSession(exchangeResult, defaultAccountType);
|
|
2029
2091
|
setState((prev) => ({
|
|
2030
2092
|
...prev,
|
|
2031
2093
|
status: "completed",
|
|
@@ -2059,7 +2121,7 @@ function useNativeAuth(options) {
|
|
|
2059
2121
|
if (response.success && response.callback_token) {
|
|
2060
2122
|
const exchangeResult = await nativeAuthService.exchange(response.callback_token);
|
|
2061
2123
|
if (exchangeResult.success) {
|
|
2062
|
-
const { user: savedUser } = saveSession(exchangeResult,
|
|
2124
|
+
const { user: savedUser } = saveSession(exchangeResult, defaultAccountType);
|
|
2063
2125
|
setState((prev) => ({
|
|
2064
2126
|
...prev,
|
|
2065
2127
|
status: "completed",
|
|
@@ -2104,14 +2166,14 @@ function useNativeAuth(options) {
|
|
|
2104
2166
|
}
|
|
2105
2167
|
}, [state.processToken]);
|
|
2106
2168
|
const setSession = react.useCallback((data) => {
|
|
2107
|
-
const { user: savedUser } = saveSession(data,
|
|
2169
|
+
const { user: savedUser } = saveSession(data, defaultAccountType);
|
|
2108
2170
|
setState((prev) => ({
|
|
2109
2171
|
...prev,
|
|
2110
2172
|
user: savedUser,
|
|
2111
2173
|
status: "completed",
|
|
2112
2174
|
processToken: null
|
|
2113
2175
|
}));
|
|
2114
|
-
}, [
|
|
2176
|
+
}, [defaultAccountType]);
|
|
2115
2177
|
const logout = react.useCallback(async () => {
|
|
2116
2178
|
const token = localStorage.getItem(STORAGE.AUTH_TOKEN) || localStorage.getItem(STORAGE.TOKEN);
|
|
2117
2179
|
try {
|
|
@@ -2271,7 +2333,7 @@ function LoginModal({
|
|
|
2271
2333
|
iamApiUrl,
|
|
2272
2334
|
loading,
|
|
2273
2335
|
showSwitchToSignup = true,
|
|
2274
|
-
|
|
2336
|
+
defaultAccountType
|
|
2275
2337
|
}) {
|
|
2276
2338
|
const {
|
|
2277
2339
|
status,
|
|
@@ -2290,7 +2352,7 @@ function LoginModal({
|
|
|
2290
2352
|
setSession,
|
|
2291
2353
|
reset: resetAuth,
|
|
2292
2354
|
clearError
|
|
2293
|
-
} = useNativeAuth({ saasApiUrl, iamApiUrl,
|
|
2355
|
+
} = useNativeAuth({ saasApiUrl, iamApiUrl, defaultAccountType });
|
|
2294
2356
|
const [step, setStep] = react.useState("choice");
|
|
2295
2357
|
const [email, setEmail] = react.useState("");
|
|
2296
2358
|
const [password, setPassword] = react.useState("");
|
|
@@ -2799,8 +2861,7 @@ function LoginModal({
|
|
|
2799
2861
|
onOpenChange: setShowPasswordRecovery,
|
|
2800
2862
|
onSuccess: () => setShowPasswordRecovery(false),
|
|
2801
2863
|
saasApiUrl,
|
|
2802
|
-
iamApiUrl
|
|
2803
|
-
debug
|
|
2864
|
+
iamApiUrl
|
|
2804
2865
|
}
|
|
2805
2866
|
)
|
|
2806
2867
|
] });
|
|
@@ -3058,8 +3119,7 @@ function useMobileRegistration(options) {
|
|
|
3058
3119
|
if (options && !configuredRef.current) {
|
|
3059
3120
|
setNativeAuthConfig({
|
|
3060
3121
|
saasApiUrl: options.saasApiUrl,
|
|
3061
|
-
iamApiUrl: options.iamApiUrl
|
|
3062
|
-
debug: options.debug
|
|
3122
|
+
iamApiUrl: options.iamApiUrl
|
|
3063
3123
|
});
|
|
3064
3124
|
configuredRef.current = true;
|
|
3065
3125
|
}
|
|
@@ -3358,7 +3418,7 @@ function SuccessOrbit() {
|
|
|
3358
3418
|
})
|
|
3359
3419
|
] });
|
|
3360
3420
|
}
|
|
3361
|
-
function SignupModal({ open, onOpenChange, onSwitchToLogin, onSignupSuccess, saasApiUrl, iamApiUrl,
|
|
3421
|
+
function SignupModal({ open, onOpenChange, onSwitchToLogin, onSignupSuccess, saasApiUrl, iamApiUrl, defaultAccountType }) {
|
|
3362
3422
|
const {
|
|
3363
3423
|
status,
|
|
3364
3424
|
formData,
|
|
@@ -3374,7 +3434,7 @@ function SignupModal({ open, onOpenChange, onSwitchToLogin, onSignupSuccess, saa
|
|
|
3374
3434
|
resendOtp,
|
|
3375
3435
|
reset: resetReg,
|
|
3376
3436
|
clearError
|
|
3377
|
-
} = useMobileRegistration({ saasApiUrl, iamApiUrl
|
|
3437
|
+
} = useMobileRegistration({ saasApiUrl, iamApiUrl });
|
|
3378
3438
|
const [step, setStep] = react.useState("intro");
|
|
3379
3439
|
const [otpCode, setOtpCode] = react.useState("");
|
|
3380
3440
|
const [password, setPassword] = react.useState("");
|
|
@@ -4075,7 +4135,7 @@ function NativeSSOPage({
|
|
|
4075
4135
|
onLoginSuccess,
|
|
4076
4136
|
onLogout,
|
|
4077
4137
|
onOnboardingComplete,
|
|
4078
|
-
|
|
4138
|
+
accountType = "user",
|
|
4079
4139
|
title = "Un compte, plusieurs accès",
|
|
4080
4140
|
description = "Connectez-vous avec votre compte Ollaid pour accéder à toutes les applications partenaires.",
|
|
4081
4141
|
logoUrl,
|
|
@@ -4093,7 +4153,7 @@ function NativeSSOPage({
|
|
|
4093
4153
|
}
|
|
4094
4154
|
return null;
|
|
4095
4155
|
});
|
|
4096
|
-
const resolvedDebug =
|
|
4156
|
+
const resolvedDebug = isDebugMode();
|
|
4097
4157
|
react.useEffect(() => {
|
|
4098
4158
|
const root = document.documentElement;
|
|
4099
4159
|
const originalValues = {};
|
|
@@ -4172,6 +4232,7 @@ function NativeSSOPage({
|
|
|
4172
4232
|
localStorage.removeItem(STORAGE.TOKEN);
|
|
4173
4233
|
localStorage.removeItem(STORAGE.USER);
|
|
4174
4234
|
localStorage.removeItem(STORAGE.ACCOUNT_TYPE);
|
|
4235
|
+
localStorage.removeItem(STORAGE.ALIAS_REFERENCE);
|
|
4175
4236
|
setSession(null);
|
|
4176
4237
|
onLogout == null ? void 0 : onLogout();
|
|
4177
4238
|
}, [onLogout]);
|
|
@@ -4270,7 +4331,7 @@ function NativeSSOPage({
|
|
|
4270
4331
|
onLoginSuccess: handleLoginSuccess,
|
|
4271
4332
|
saasApiUrl,
|
|
4272
4333
|
iamApiUrl,
|
|
4273
|
-
|
|
4334
|
+
defaultAccountType: accountType
|
|
4274
4335
|
}
|
|
4275
4336
|
),
|
|
4276
4337
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4284,7 +4345,7 @@ function NativeSSOPage({
|
|
|
4284
4345
|
onSignupSuccess: handleLoginSuccess,
|
|
4285
4346
|
saasApiUrl,
|
|
4286
4347
|
iamApiUrl,
|
|
4287
|
-
|
|
4348
|
+
defaultAccountType: accountType
|
|
4288
4349
|
}
|
|
4289
4350
|
),
|
|
4290
4351
|
pendingSession && /* @__PURE__ */ jsxRuntime.jsx(
|