@ollaid/native-sso 2.8.2 → 2.8.4

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.
@@ -0,0 +1,139 @@
1
+ # Update Infos
2
+
3
+ Version: `2.8.4`
4
+
5
+ Ce document décrit le flux recommandé pour la modal profil `OnboardingModal` quand un SaaS veut synchroniser les informations utilisateur avec le package `@ollaid/native-sso`.
6
+
7
+ ## Objectif
8
+
9
+ - IAM reste la source de vérité.
10
+ - Le SaaS garde une copie locale de `user.user_infos`.
11
+ - Quand le modal s’ouvre, il hydrate les champs depuis IAM.
12
+ - Quand l’utilisateur enregistre, IAM est mis à jour en premier.
13
+ - Après succès IAM, le SaaS est mis à jour immédiatement.
14
+
15
+ ## Flux recommandé
16
+
17
+ ### 1. Ouverture du modal
18
+
19
+ Quand le SaaS ouvre la modal profil:
20
+
21
+ - afficher `OnboardingModal`
22
+ - passer le `user` courant
23
+ - utiliser `variant="edit"` si l’utilisateur modifie son profil depuis le SaaS
24
+ - garder `profileHydrating={true}` tant que les données ne sont pas encore chargées
25
+
26
+ Si vous n’utilisez pas `NativeSSOPage`, la couche SaaS doit d’abord hydrater le profil depuis IAM, puis ouvrir la modal avec les données reçues.
27
+
28
+ Pendant cette phase, la modal doit afficher un spinner de chargement.
29
+
30
+ ### 2. Hydratation depuis IAM
31
+
32
+ Le package récupère les infos depuis IAM via son service profil:
33
+
34
+ - `profileService.getProfile()`
35
+ - endpoint IAM: `GET /backoffice/profile`
36
+
37
+ Dans le flux autonome `NativeSSOPage`, cette hydratation est déjà gérée par le package.
38
+
39
+ Les données récupérées doivent remplir les inputs du formulaire:
40
+
41
+ - `name`
42
+ - `image_url`
43
+ - `ccphone`
44
+ - `phone`
45
+ - `email`
46
+ - `address`
47
+ - `town`
48
+ - `country`
49
+
50
+ Si IAM renvoie une donnée plus fraîche que celle du SaaS, la valeur IAM gagne.
51
+
52
+ ### 3. Mise à jour depuis la modal
53
+
54
+ Quand l’utilisateur clique sur `Enregistrer`:
55
+
56
+ - le package envoie l’update vers IAM via `profileService.updateProfile()`
57
+ - endpoint IAM: `PUT /backoffice/profile`
58
+ - IAM valide, normalise et renvoie la version finale du profil
59
+
60
+ IAM reste prioritaire sur la validation et la normalisation des champs.
61
+
62
+ ### 4. Synchronisation immédiate vers le SaaS
63
+
64
+ Après succès IAM:
65
+
66
+ - le package appelle `onComplete`
67
+ - `onComplete` contient les données finales sous forme `user_infos`
68
+ - le SaaS doit persister immédiatement ces données dans son propre utilisateur
69
+
70
+ Le SaaS peut:
71
+
72
+ - mettre à jour son cache local
73
+ - appeler son backend pour enregistrer `user.user_infos`
74
+ - recharger la session utilisateur si besoin
75
+
76
+ ## Règle d’or
77
+
78
+ La hiérarchie correcte est:
79
+
80
+ 1. IAM
81
+ 2. package SSO
82
+ 3. SaaS
83
+
84
+ Donc:
85
+
86
+ - lecture depuis IAM
87
+ - écriture vers IAM
88
+ - réplication immédiate vers le SaaS
89
+
90
+ ## Exemple d’intégration SaaS
91
+
92
+ ```tsx
93
+ import { useState } from 'react';
94
+ import { OnboardingModal } from '@ollaid/native-sso';
95
+
96
+ export function ProfileSettings({ user, api }: { user: NativeUser; api: any }) {
97
+ const [open, setOpen] = useState(false);
98
+
99
+ return (
100
+ <>
101
+ <button type="button" onClick={() => setOpen(true)}>
102
+ Modifier mon profil
103
+ </button>
104
+
105
+ <OnboardingModal
106
+ open={open}
107
+ onOpenChange={setOpen}
108
+ onDismiss={() => setOpen(false)}
109
+ user={user}
110
+ variant="edit"
111
+ profileHydrating={false}
112
+ onComplete={async (data) => {
113
+ await api.updateCurrentUser({
114
+ user_infos: data.user_infos,
115
+ });
116
+ setOpen(false);
117
+ }}
118
+ onSkip={() => setOpen(false)}
119
+ />
120
+ </>
121
+ );
122
+ }
123
+ ```
124
+
125
+ ## Cas du mode automatique
126
+
127
+ Quand `NativeSSOPage` gère l’auto-onboarding:
128
+
129
+ - la modal s’ouvre si le profil est incomplet
130
+ - le package hydrate les données depuis IAM
131
+ - les champs manquants sont affichés
132
+ - à la validation, IAM est mis à jour puis le SaaS est synchronisé
133
+
134
+ ## Ce que le SaaS doit retenir
135
+
136
+ - N’inversez pas la priorité des sources.
137
+ - Ne traitez pas le SaaS comme source de vérité du profil.
138
+ - Ne contournez pas `onComplete`.
139
+ - Si le SaaS modifie `user.user_infos` hors modal, il doit garder la même logique de miroir vers IAM au prochain refresh.
@@ -0,0 +1,84 @@
1
+ # Password Magic Link Flow
2
+
3
+ Version: `2.8.4`
4
+
5
+ Ce document décrit le flux recommandé pour ouvrir le modal de changement de mot de passe sans faire d'appel direct du navigateur vers IAM.
6
+
7
+ ## Objectif
8
+
9
+ - Le frontend SaaS ouvre la modal mot de passe.
10
+ - Le package `@ollaid/native-sso` appelle le backend SaaS.
11
+ - Le backend SaaS appelle IAM en server-to-server sur `POST /api/backoffice/auth/password-link`.
12
+ - IAM renvoie un `magic_token` ou une URL de redirection.
13
+ - Le backend SaaS renvoie au package une URL de redirection prête à ouvrir.
14
+ - Le navigateur est redirigé vers IAM.
15
+
16
+ ## Pourquoi ce flux
17
+
18
+ - Il évite le CORS entre navigateur et IAM pour cette action.
19
+ - Il centralise l'authentification et la journalisation côté SaaS.
20
+ - Il laisse IAM gérer la génération du lien magique.
21
+
22
+ ## Contrat attendu côté SaaS
23
+
24
+ Endpoint conseillé:
25
+
26
+ ```http
27
+ POST /api/native/password-link
28
+ ```
29
+
30
+ Headers envoyés par le package:
31
+
32
+ - `Authorization: Bearer <token>` si disponible
33
+ - `X-App-Access-Token-Ref` si présent dans le storage
34
+ - `X-IAM-Config-Prefix` si le package est configuré avec un préfixe multi-tenant
35
+ - `X-Device-*` pour le contexte appareil
36
+
37
+ Body:
38
+
39
+ ```json
40
+ {}
41
+ ```
42
+
43
+ Réponse recommandée:
44
+
45
+ ```json
46
+ {
47
+ "success": true,
48
+ "redirect_url": "https://identityam.ollaid.com/auth/auto-connect?magic_token=xxx",
49
+ "magic_token": "xxx",
50
+ "expires_at": "2026-05-18T12:00:00+00:00"
51
+ }
52
+ ```
53
+
54
+ Le backend SaaS peut aussi renvoyer `sso_url` pour compatibilité, mais `redirect_url` est la forme à privilégier.
55
+
56
+ ## Contrat backend SaaS -> IAM
57
+
58
+ Le backend SaaS doit appeler IAM en interne:
59
+
60
+ ```http
61
+ POST https://identityam.ollaid.com/api/backoffice/auth/password-link
62
+ ```
63
+
64
+ IAM répond avec:
65
+
66
+ ```json
67
+ {
68
+ "success": true,
69
+ "magic_token": "xxx",
70
+ "sso_url": "https://identityam.ollaid.com/auth/auto-connect?magic_token=xxx",
71
+ "expires_at": "2026-05-18T12:00:00+00:00"
72
+ }
73
+ ```
74
+
75
+ Le backend SaaS doit ensuite:
76
+
77
+ 1. valider l'utilisateur courant
78
+ 2. relayer la requête vers IAM
79
+ 3. retourner au package une URL de redirection prête à ouvrir
80
+
81
+ ## Intégration package
82
+
83
+ Le composant `PasswordRedirectModal` du package utilise `saasApiUrl`.
84
+ `iamApiUrl` reste supporté temporairement pour compatibilité ascendante, mais il ne doit plus être utilisé pour ce flux.
package/docs/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # Native SSO Docs
2
+
3
+ Version: `2.8.4`
4
+
5
+ Docs courtes et pratiques pour `@ollaid/native-sso`.
6
+
7
+ ## Démarrage
8
+
9
+ 1. Lisez [Quick Start](./1_quick-start.md)
10
+ 2. Vérifiez le contrat backend dans [Backend Contract](./2_backend-contract.md)
11
+ 3. Vérifiez le stockage et la sécurité dans [Storage & Security](./3_storage-security.md)
12
+ 4. Vérifiez les webhooks dans [Webhooks](./4_webhooks.md)
13
+ 5. Si vous utilisez les APIs IAM Account server-to-server, lisez [IAM Account](./5_iam-account.md)
14
+ 6. Si vous utilisez les helpers avancés, lisez [Advanced Services](./6_advanced-services.md)
15
+ 7. Consultez les notes de migration dans [Migration Notes](./7_migration-notes.md)
16
+ 8. Pour la synchro bidirectionnelle des infos profil, lisez [Update Infos](./8_update_infos.md)
17
+ 9. Pour le flux de redirection mot de passe via backend SaaS, lisez [Password Magic Link](./9_password_magic_link.md)
18
+ 10. Pour la redirection après connexion et le flux `needs_access`, lisez [Login Redirect & Access Flow](./10_login-redirect.md)
19
+ 11. Pour la persistance des sessions, le refresh token et la résilience réseau, lisez [Session Refresh & Resilience](./11_session-refresh-resilience.md)
20
+ 12. Pour le détail du flux `needs_access` et du bypass `BYPASS=true`, lisez [Needs Access & Bypass](./12_login_mode.md)
21
+ 13. Pour la vérification finale des fonctionnalités critiques, lisez [Must Have Working](./13_must_have_working.md)
22
+ 14. Pour les variables d'environnement backend IAM SSO, lisez [Backend Env Key](./14_backend_env_key.md)
23
+
24
+ ## Principe
25
+
26
+ - Le package chiffre les données de session au repos dans son storage.
27
+ - Le frontend SaaS ne lit pas le storage directement.
28
+ - Le frontend SaaS demande la session au package via `getSsoSessionSnapshot()`.
29
+ - La déconnexion passe par `logout()`.
30
+ - Les APIs IAM Account (`iamAccountService`) sont server-to-server uniquement.
31
+ - Les helpers avancés (`mobilePasswordService`, `profileChangeService`, `profileMediaService`) sont documentés séparément.
32
+
33
+ ## Checklist SaaS
34
+
35
+ - Fournir `redirectAfterLogin` sur `NativeSSOPage` si vous ne voulez pas rester sur la page SSO après connexion.
36
+ - Fournir `redirectAfterLogout` si vous voulez une sortie vers une page métier.
37
+ - Côté backend, renvoyer `needs_access` quand l'utilisateur est authentifié mais n'a pas encore accès à cette application.
38
+ - Si le SaaS active `BYPASS=true`, le backend peut auto-créer l'accès et finaliser la connexion sans afficher le modal.
39
+ - Ne pas appeler le callback final de redirection tant que `grantAccess()` n'a pas terminé, sauf si `BYPASS=true`.
40
+
41
+ ## Onboarding profil
42
+
43
+ La modal `OnboardingModal` gère deux modes:
44
+
45
+ - `missing`:
46
+ - `name`
47
+ - `image_url`
48
+ - `phone`
49
+ - `email`
50
+ - `address`
51
+ - `town`
52
+ - `country`
53
+ - `edit`:
54
+ - nom complet
55
+ - photo de profil
56
+ - téléphone
57
+ - email
58
+ - adresse
59
+ - ville
60
+ - pays
61
+
62
+ Le mode automatique piloté par `NativeSSOPage` ouvre la modal après le délai configuré si l’un de ces champs est manquant. Le modal reste scrollable à l’intérieur via le conteneur de contenu dédié.
63
+
64
+ Quand vous ouvrez la modal depuis votre SaaS, utilisez `variant="edit"` pour afficher le formulaire complet éditable.
65
+
66
+ Le changement de téléphone ou d’email ouvre un sous-flux OTP dans la même modal, avec une fermeture bloquée tant que le flux est en cours.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ollaid/native-sso",
3
- "version": "2.8.2",
3
+ "version": "2.8.4",
4
4
  "description": "Package NPM fullstack pour l'authentification Native SSO Ollaid - Frontend-First (Link APIs & Refresh support)",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -14,11 +14,14 @@
14
14
  }
15
15
  },
16
16
  "files": [
17
- "dist"
17
+ "dist",
18
+ "docs",
19
+ "scripts"
18
20
  ],
19
21
  "scripts": {
20
22
  "build": "vite build && tsc --emitDeclarationOnly --declaration --outDir dist",
21
- "dev": "vite build --watch"
23
+ "dev": "vite build --watch",
24
+ "postinstall": "node ./scripts/copy-docs.mjs"
22
25
  },
23
26
  "peerDependencies": {
24
27
  "@capacitor/device": "^7.0.0 || ^8.0.0",
@@ -0,0 +1,36 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import process from 'node:process';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const packageRoot = path.dirname(fileURLToPath(import.meta.url));
7
+ const docsSourceDir = path.resolve(packageRoot, '../docs');
8
+ const projectRoot = process.env.INIT_CWD
9
+ ? path.resolve(process.env.INIT_CWD)
10
+ : process.cwd();
11
+ const targetDir = path.resolve(projectRoot, 'docs/ollaid@sso_package');
12
+
13
+ async function main() {
14
+ let entries;
15
+ try {
16
+ entries = await fs.readdir(docsSourceDir, { withFileTypes: true });
17
+ } catch {
18
+ return;
19
+ }
20
+
21
+ await fs.rm(targetDir, { recursive: true, force: true });
22
+ await fs.mkdir(targetDir, { recursive: true });
23
+
24
+ for (const entry of entries) {
25
+ if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
26
+
27
+ const sourceFile = path.join(docsSourceDir, entry.name);
28
+ const targetFile = path.join(targetDir, entry.name);
29
+ await fs.copyFile(sourceFile, targetFile);
30
+ }
31
+ }
32
+
33
+ main().catch((error) => {
34
+ console.error('[ollaid-native-sso] doc copy failed:', error);
35
+ process.exitCode = 1;
36
+ });