@ollaid/native-sso 1.0.6 → 1.0.8
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 +185 -13
- package/dist/components/LoginModal.d.ts +3 -1
- package/dist/components/PhoneInput.d.ts +1 -0
- package/dist/components/SignupModal.d.ts +3 -1
- package/dist/components/ui.d.ts +10 -0
- package/dist/hooks/useMobileRegistration.d.ts +1 -1
- package/dist/index.cjs +772 -359
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +772 -359
- package/dist/index.js.map +1 -1
- package/dist/utils/countries.d.ts +19 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,19 +11,22 @@ Package NPM Frontend-First pour l'authentification Native SSO Ollaid.
|
|
|
11
11
|
2. [Intégration rapide (3 étapes)](#intégration-rapide-3-étapes)
|
|
12
12
|
3. [Props de NativeSSOPage](#props-de-nativessopage)
|
|
13
13
|
4. [Usage avancé (composants individuels)](#usage-avancé-composants-individuels)
|
|
14
|
-
5. [
|
|
15
|
-
6. [
|
|
16
|
-
7. [
|
|
17
|
-
8. [
|
|
18
|
-
9. [
|
|
19
|
-
10. [
|
|
20
|
-
11. [
|
|
21
|
-
12. [
|
|
22
|
-
13. [
|
|
23
|
-
14. [
|
|
24
|
-
15. [
|
|
25
|
-
16. [
|
|
26
|
-
17. [
|
|
14
|
+
5. [Props des composants individuels](#props-des-composants-individuels)
|
|
15
|
+
6. [Gestion des conflits d'inscription](#gestion-des-conflits-dinscription)
|
|
16
|
+
7. [Hook useMobileRegistration](#hook-usemobileregistration)
|
|
17
|
+
8. [Backend SaaS — Endpoints requis](#backend-saas--endpoints-requis)
|
|
18
|
+
9. [APIs IAM Account (Server-to-Server)](#apis-iam-account-server-to-server)
|
|
19
|
+
10. [Avatar par application](#avatar-par-application)
|
|
20
|
+
11. [Réponses d'erreur](#réponses-derreur)
|
|
21
|
+
12. [Configuration .env Laravel](#configuration-env-laravel)
|
|
22
|
+
13. [Migration Laravel](#migration-laravel)
|
|
23
|
+
14. [Flux d'authentification](#flux-dauthentification)
|
|
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)
|
|
27
30
|
|
|
28
31
|
---
|
|
29
32
|
|
|
@@ -414,6 +417,7 @@ function MyCustomAuth() {
|
|
|
414
417
|
onLoginSuccess={(token, user) => console.log('OK', user)}
|
|
415
418
|
saasApiUrl="https://mon-saas.com/api"
|
|
416
419
|
iamApiUrl="https://identityam.ollaid.com/api"
|
|
420
|
+
defaultAccountType="user"
|
|
417
421
|
/>
|
|
418
422
|
</>
|
|
419
423
|
);
|
|
@@ -422,6 +426,170 @@ function MyCustomAuth() {
|
|
|
422
426
|
|
|
423
427
|
---
|
|
424
428
|
|
|
429
|
+
## Props des composants individuels
|
|
430
|
+
|
|
431
|
+
### `SignupModal`
|
|
432
|
+
|
|
433
|
+
| Prop | Type | Requis | Description |
|
|
434
|
+
|------|------|--------|-------------|
|
|
435
|
+
| `open` | `boolean` | ✅ | Contrôle l'ouverture du modal |
|
|
436
|
+
| `onOpenChange` | `(open: boolean) => void` | ✅ | Callback de changement d'état |
|
|
437
|
+
| `onSwitchToLogin` | `() => void` | ✅ | Callback pour basculer vers le login |
|
|
438
|
+
| `onSignupSuccess` | `(token: string, user: UserInfos) => void` | ✅ | Callback après inscription réussie |
|
|
439
|
+
| `saasApiUrl` | `string` | ✅ | URL du backend SaaS |
|
|
440
|
+
| `iamApiUrl` | `string` | ✅ | URL du backend IAM |
|
|
441
|
+
| `defaultAccountType` | `'user' \| 'client'` | ❌ | Hérité de `NativeSSOPage.accountType`. Type de compte à persister dans localStorage. Si non défini, la valeur par défaut est `'user'`. |
|
|
442
|
+
| `onSwitchToLoginWithPhone` | `(phone: string) => void` | ❌ | Callback lors d'un conflit phone +221 — permet de basculer vers `LoginModal` avec le numéro pré-rempli |
|
|
443
|
+
|
|
444
|
+
### `LoginModal`
|
|
445
|
+
|
|
446
|
+
| Prop | Type | Requis | Description |
|
|
447
|
+
|------|------|--------|-------------|
|
|
448
|
+
| `open` | `boolean` | ✅ | Contrôle l'ouverture du modal |
|
|
449
|
+
| `onOpenChange` | `(open: boolean) => void` | ✅ | Callback de changement d'état |
|
|
450
|
+
| `onSwitchToSignup` | `() => void` | ✅ | Callback pour basculer vers l'inscription |
|
|
451
|
+
| `onLoginSuccess` | `(token: string, user: UserInfos) => void` | ❌ | Callback après connexion réussie |
|
|
452
|
+
| `saasApiUrl` | `string` | ✅ | URL du backend SaaS |
|
|
453
|
+
| `iamApiUrl` | `string` | ✅ | URL du backend IAM |
|
|
454
|
+
| `loading` | `boolean` | ❌ | État de chargement externe |
|
|
455
|
+
| `showSwitchToSignup` | `boolean` | ❌ | Afficher le lien "Pas de compte ? S'inscrire" (défaut: `true`) |
|
|
456
|
+
| `defaultAccountType` | `'user' \| 'client'` | ❌ | Hérité de `NativeSSOPage.accountType`. Type de compte à persister dans localStorage. Si non défini, la valeur par défaut est `'user'`. |
|
|
457
|
+
| `initialPhone` | `string` | ❌ | Pré-remplit le numéro de téléphone et va directement à l'étape `phone-input`. Utilisé par le hand-off depuis `SignupModal` lors d'un conflit. |
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## Gestion des conflits d'inscription
|
|
462
|
+
|
|
463
|
+
Lorsqu'un utilisateur tente de s'inscrire avec un email ou un téléphone déjà associé à un compte existant, le backend retourne un **409 Conflict**. Le `SignupModal` gère automatiquement ce cas avec une vue dédiée (`ConflictView`).
|
|
464
|
+
|
|
465
|
+
### Comportement automatique
|
|
466
|
+
|
|
467
|
+
1. L'utilisateur remplit le formulaire d'inscription et soumet
|
|
468
|
+
2. Le backend détecte un conflit (`email_exists` ou `phone_exists`) et retourne un objet `conflict`
|
|
469
|
+
3. Le `SignupModal` affiche la `ConflictView` avec les options disponibles
|
|
470
|
+
|
|
471
|
+
### Affichage intelligent des identifiants
|
|
472
|
+
|
|
473
|
+
- **L'identifiant saisi par l'utilisateur** (email ou téléphone) est affiché **en clair** pour confirmer sa saisie
|
|
474
|
+
- **L'identifiant lié au compte existant** (ex: l'email masqué associé à un numéro déjà enregistré) reste **masqué** pour la confidentialité (ex: `j***@gmail.com`)
|
|
475
|
+
|
|
476
|
+
### Options proposées
|
|
477
|
+
|
|
478
|
+
Selon le type de conflit et les capacités du compte existant, la vue propose :
|
|
479
|
+
|
|
480
|
+
| Option | Condition | Action |
|
|
481
|
+
|--------|-----------|--------|
|
|
482
|
+
| Se connecter | `conflict.options.can_login === true` | Bascule vers `LoginModal` via `onSwitchToLogin` |
|
|
483
|
+
| Se connecter par téléphone | Conflit phone + numéro +221 | Bascule vers `LoginModal` avec le numéro pré-rempli via `onSwitchToLoginWithPhone(phone)` |
|
|
484
|
+
| Récupérer par email | `conflict.options.can_recover_by_email === true` | Ouvre le `PasswordRecoveryModal` |
|
|
485
|
+
| Récupérer par SMS | `conflict.options.can_recover_by_sms === true` | Ouvre le `PasswordRecoveryModal` |
|
|
486
|
+
| Modifier les informations | Toujours disponible | Retour au formulaire pour changer l'identifiant |
|
|
487
|
+
|
|
488
|
+
### Exemple d'intégration avec hand-off téléphone
|
|
489
|
+
|
|
490
|
+
```tsx
|
|
491
|
+
function AuthPage() {
|
|
492
|
+
const [showSignup, setShowSignup] = useState(false);
|
|
493
|
+
const [showLogin, setShowLogin] = useState(false);
|
|
494
|
+
const [loginPhone, setLoginPhone] = useState<string | undefined>();
|
|
495
|
+
|
|
496
|
+
return (
|
|
497
|
+
<>
|
|
498
|
+
<SignupModal
|
|
499
|
+
open={showSignup}
|
|
500
|
+
onOpenChange={setShowSignup}
|
|
501
|
+
onSwitchToLogin={() => { setShowSignup(false); setShowLogin(true); }}
|
|
502
|
+
onSwitchToLoginWithPhone={(phone) => {
|
|
503
|
+
setLoginPhone(phone);
|
|
504
|
+
setShowSignup(false);
|
|
505
|
+
setShowLogin(true);
|
|
506
|
+
}}
|
|
507
|
+
onSignupSuccess={(token, user) => navigate('/dashboard')}
|
|
508
|
+
saasApiUrl="https://mon-saas.com/api"
|
|
509
|
+
iamApiUrl="https://identityam.ollaid.com/api"
|
|
510
|
+
/>
|
|
511
|
+
|
|
512
|
+
<LoginModal
|
|
513
|
+
open={showLogin}
|
|
514
|
+
onOpenChange={setShowLogin}
|
|
515
|
+
onSwitchToSignup={() => { setShowLogin(false); setShowSignup(true); }}
|
|
516
|
+
initialPhone={loginPhone}
|
|
517
|
+
saasApiUrl="https://mon-saas.com/api"
|
|
518
|
+
iamApiUrl="https://identityam.ollaid.com/api"
|
|
519
|
+
/>
|
|
520
|
+
</>
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### Type `RegistrationConflict`
|
|
526
|
+
|
|
527
|
+
L'objet retourné par le backend lors d'un conflit :
|
|
528
|
+
|
|
529
|
+
```typescript
|
|
530
|
+
interface RegistrationConflict {
|
|
531
|
+
type: 'email' | 'phone';
|
|
532
|
+
masked_identifier: string;
|
|
533
|
+
options: {
|
|
534
|
+
can_login: boolean;
|
|
535
|
+
can_recover_by_email: boolean;
|
|
536
|
+
can_recover_by_sms: boolean;
|
|
537
|
+
masked_email?: string;
|
|
538
|
+
masked_phone?: string;
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
## Hook `useMobileRegistration`
|
|
546
|
+
|
|
547
|
+
Hook React pour gérer le flow d'inscription mobile en 3 étapes : `init` → `verify-otp` → `complete`.
|
|
548
|
+
|
|
549
|
+
### Import
|
|
550
|
+
|
|
551
|
+
```tsx
|
|
552
|
+
import { useMobileRegistration } from '@ollaid/native-sso';
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### Propriétés retournées
|
|
556
|
+
|
|
557
|
+
#### État
|
|
558
|
+
|
|
559
|
+
| Propriété | Type | Description |
|
|
560
|
+
|-----------|------|-------------|
|
|
561
|
+
| `processToken` | `string \| null` | Token de processus d'inscription en cours |
|
|
562
|
+
| `status` | `'idle' \| 'pending_otp' \| 'pending_password' \| 'pending_registration' \| 'completed'` | Étape actuelle du flow |
|
|
563
|
+
| `formData` | `Partial<MobileRegistrationFormData>` | Données du formulaire stockées |
|
|
564
|
+
| `loading` | `boolean` | Indique si une opération est en cours |
|
|
565
|
+
| `error` | `string \| null` | Message d'erreur (français, user-friendly) |
|
|
566
|
+
| `conflict` | `RegistrationConflict \| null` | Objet de conflit si l'identifiant existe déjà |
|
|
567
|
+
| `isCompleted` | `boolean` | `true` si l'inscription est terminée |
|
|
568
|
+
| `hasConflict` | `boolean` | `true` si un conflit est en cours |
|
|
569
|
+
|
|
570
|
+
#### Type de compte (phone-only)
|
|
571
|
+
|
|
572
|
+
| Propriété | Type | Description |
|
|
573
|
+
|-----------|------|-------------|
|
|
574
|
+
| `accountType` | `'email' \| 'phone-only'` | Type de compte sélectionné |
|
|
575
|
+
| `setAccountType` | `(type: AccountType) => void` | Change le type de compte |
|
|
576
|
+
| `isPhoneOnly` | `boolean` | `true` si `accountType === 'phone-only'` |
|
|
577
|
+
|
|
578
|
+
#### Méthodes
|
|
579
|
+
|
|
580
|
+
| Méthode | Signature | Description |
|
|
581
|
+
|---------|-----------|-------------|
|
|
582
|
+
| `updateFormData` | `(data: Partial<MobileRegistrationFormData>) => void` | Met à jour les données du formulaire |
|
|
583
|
+
| `initRegistration` | `(data) => Promise<{ success, otp_code_dev?, otp_method?, otp_sent_to? }>` | Initialise l'inscription (envoi OTP). Peut retourner un conflit. |
|
|
584
|
+
| `verifyOtp` | `(otpCode: string) => Promise<{ success, completed?, callback_token? }>` | Vérifie le code OTP |
|
|
585
|
+
| `completeRegistration` | `(password: string) => Promise<{ success, callback_token? }>` | Finalise avec mot de passe (type `email`) |
|
|
586
|
+
| `completePhoneOnlyRegistration` | `() => Promise<{ success, callback_token? }>` | Finalise sans mot de passe (type `phone-only`, Sénégal 🇸🇳) |
|
|
587
|
+
| `resendOtp` | `() => Promise<{ success, cooldown?, otp_code_dev? }>` | Renvoie le code OTP |
|
|
588
|
+
| `reset` | `() => void` | Réinitialise tout le hook |
|
|
589
|
+
| `clearError` | `() => void` | Efface l'erreur et le conflit |
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
425
593
|
## Backend SaaS — Endpoints requis
|
|
426
594
|
|
|
427
595
|
Le backend SaaS (Laravel) doit exposer **4 endpoints**. Voici les spécifications exactes :
|
|
@@ -1883,6 +2051,10 @@ if (config('services.iam.debug')) {
|
|
|
1883
2051
|
|
|
1884
2052
|
### Types
|
|
1885
2053
|
- `UserInfos`, `NativeAuthState`, `NativeAuthStatus`, `NativeCredentials`, etc.
|
|
2054
|
+
- `AccountType` — `'email' | 'phone-only'`
|
|
2055
|
+
- `MobilePasswordState`, `MobilePasswordStatus` — Types pour la récupération de mot de passe
|
|
2056
|
+
- `MobileRegistrationFormData` — Données du formulaire d'inscription
|
|
2057
|
+
- `RegistrationConflict` — Objet de conflit d'inscription (type interne, voir [Gestion des conflits](#gestion-des-conflits-dinscription))
|
|
1886
2058
|
- `LinkPhoneRequest`, `LinkPhoneResponse` — Types pour l'API link-phone
|
|
1887
2059
|
- `LinkEmailRequest`, `LinkEmailResponse` — Types pour l'API link-email
|
|
1888
2060
|
- `RefreshUserInfoSingleRequest`, `RefreshUserInfoSingleResponse` — Types pour refresh single
|
|
@@ -16,6 +16,8 @@ export interface LoginModalProps {
|
|
|
16
16
|
showSwitchToSignup?: boolean;
|
|
17
17
|
/** Type de compte par défaut à persister dans localStorage */
|
|
18
18
|
defaultAccountType?: 'user' | 'client';
|
|
19
|
+
/** Pre-fill phone number and go directly to phone-input step */
|
|
20
|
+
initialPhone?: string;
|
|
19
21
|
}
|
|
20
|
-
export declare function LoginModal({ open, onOpenChange, onSwitchToSignup, onLoginSuccess, saasApiUrl, iamApiUrl, loading, showSwitchToSignup, defaultAccountType, }: LoginModalProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export declare function LoginModal({ open, onOpenChange, onSwitchToSignup, onLoginSuccess, saasApiUrl, iamApiUrl, loading, showSwitchToSignup, defaultAccountType, initialPhone, }: LoginModalProps): import("react/jsx-runtime").JSX.Element;
|
|
21
23
|
export default LoginModal;
|
|
@@ -14,6 +14,8 @@ export interface SignupModalProps {
|
|
|
14
14
|
iamApiUrl: string;
|
|
15
15
|
/** Type de compte par défaut à persister dans localStorage */
|
|
16
16
|
defaultAccountType?: 'user' | 'client';
|
|
17
|
+
/** Called when conflict resolution wants to switch to login with a pre-filled phone */
|
|
18
|
+
onSwitchToLoginWithPhone?: (phone: string) => void;
|
|
17
19
|
}
|
|
18
|
-
export declare function SignupModal({ open, onOpenChange, onSwitchToLogin, onSignupSuccess, saasApiUrl, iamApiUrl, defaultAccountType }: SignupModalProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export declare function SignupModal({ open, onOpenChange, onSwitchToLogin, onSignupSuccess, saasApiUrl, iamApiUrl, defaultAccountType, onSwitchToLoginWithPhone }: SignupModalProps): import("react/jsx-runtime").JSX.Element;
|
|
19
21
|
export default SignupModal;
|
package/dist/components/ui.d.ts
CHANGED
|
@@ -34,6 +34,16 @@ export declare function DialogContent({ children, className }: {
|
|
|
34
34
|
children: React.ReactNode;
|
|
35
35
|
className?: string;
|
|
36
36
|
}): import("react/jsx-runtime").JSX.Element;
|
|
37
|
+
export declare function DialogBody({ children, className, style }: {
|
|
38
|
+
children: React.ReactNode;
|
|
39
|
+
className?: string;
|
|
40
|
+
style?: React.CSSProperties;
|
|
41
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
42
|
+
export declare function DialogFooter({ children, className, style }: {
|
|
43
|
+
children: React.ReactNode;
|
|
44
|
+
className?: string;
|
|
45
|
+
style?: React.CSSProperties;
|
|
46
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
37
47
|
export declare function DialogHeader({ children, className, style }: {
|
|
38
48
|
children: React.ReactNode;
|
|
39
49
|
className?: string;
|
|
@@ -41,7 +41,7 @@ export declare function useMobileRegistration(options?: UseMobileRegistrationOpt
|
|
|
41
41
|
error_type?: undefined;
|
|
42
42
|
} | {
|
|
43
43
|
success: boolean;
|
|
44
|
-
error_type:
|
|
44
|
+
error_type: any;
|
|
45
45
|
otp_code_dev?: undefined;
|
|
46
46
|
otp_method?: undefined;
|
|
47
47
|
otp_sent_to?: undefined;
|