@ollaid/native-sso 2.8.2 → 2.8.3

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,54 @@
1
+ # Storage & Security
2
+
3
+ Version: `2.8.3`
4
+
5
+ ## Ce que fait le package
6
+
7
+ - Les données de session sont stockées avec des clés préfixées `sso_`.
8
+ - Le contenu persistant est chiffré au repos par défaut.
9
+ - Le frontend hôte lit la session via `getSsoSessionSnapshot()`.
10
+ - Le package gère aussi des clés internes de rappel profil `sso_image_*` pour l'onboarding et le snooze.
11
+
12
+ ### Exemple de lecture
13
+
14
+ ```ts
15
+ import { getSsoSessionSnapshot } from '@ollaid/native-sso';
16
+
17
+ const session = getSsoSessionSnapshot();
18
+
19
+ console.log(session.authToken);
20
+ console.log(session.user);
21
+ ```
22
+
23
+ ## Ce que le SaaS doit retenir
24
+
25
+ - Le SaaS ne lit pas le storage directement.
26
+ - Le SaaS demande la session au package quand il en a besoin.
27
+ - Les helpers `getAuthToken()`, `getAuthUser()` et `getAccountType()` restent compatibles, mais l’API recommandée est `getSsoSessionSnapshot()`.
28
+ - Si le backend fournit `refresh_token` / `refresh_expires_at`, le package les persiste et peut tenter un refresh avant déconnexion.
29
+
30
+ ### Quand utiliser quoi
31
+
32
+ - `getSsoSessionSnapshot()` : lecture standard côté frontend SaaS.
33
+ - `getAuthToken()` : compatibilité rapide.
34
+ - `getAuthUser()` : compatibilité rapide.
35
+ - `getAccountType()` : compatibilité rapide.
36
+
37
+ ## Recommandation
38
+
39
+ - **Web**: storage chiffré du package ou session serveur selon votre architecture.
40
+ - **Capacitor**: fournissez un storage natif sécurisé si vous pouvez.
41
+ - **Logout**: utilisez toujours `logout()`.
42
+
43
+ ## Refresh token et persistance
44
+
45
+ - Si le backend fournit `refresh_token` / `refresh_expires_at`, le package les persiste et peut tenter un refresh avant déconnexion.
46
+ - Le backend SaaS doit conserver un `refresh_token_hash` côté base pour que la rotation fonctionne.
47
+ - Une erreur réseau sur `refresh()` ne doit pas vider la session. Voir [Session Refresh & Resilience](./11_session-refresh-resilience.md).
48
+
49
+ ## Anti-patterns
50
+
51
+ - Ne pas lire `localStorage` directement depuis le frontend hôte.
52
+ - Ne pas utiliser `clearAuthToken()` comme flux principal.
53
+ - Ne pas effacer le storage manuellement pour simuler une déconnexion.
54
+ - Ne pas compter sur le chiffrement local comme protection absolue contre du JavaScript compromis.
@@ -0,0 +1,48 @@
1
+ # Webhooks
2
+
3
+ Version: `2.8.3`
4
+
5
+ ## But
6
+
7
+ Le backend IAM envoie des webhooks pour prévenir le SaaS d’une révocation, d’une suspension ou d’une mise à jour utilisateur.
8
+
9
+ ## Points clés
10
+
11
+ - Vérifiez `X-IAM-Signature`.
12
+ - Dédupliquez `X-IAM-Webhook-Id`.
13
+ - Révoquez uniquement la session ciblée.
14
+
15
+ ## Exemple de payload
16
+
17
+ Headers:
18
+
19
+ ```http
20
+ X-IAM-Signature: sha256=...
21
+ X-IAM-Webhook-Id: wh_123
22
+ ```
23
+
24
+ Body:
25
+
26
+ ```json
27
+ {
28
+ "type": "session",
29
+ "event": "revoked",
30
+ "timestamp": "2026-05-15T17:00:00Z",
31
+ "data": {
32
+ "iam_reference": "USR-XXXX",
33
+ "app_access_token_ref": "aat_xxx"
34
+ }
35
+ }
36
+ ```
37
+
38
+ ## Événements utiles
39
+
40
+ - `session.revoked`
41
+ - `access.revoked`
42
+ - `user.suspended`
43
+ - `user.updated`
44
+
45
+ ## Règle pratique
46
+
47
+ - Ne faites pas de révocation globale par défaut.
48
+ - Utilisez `app_access_token_ref` pour cibler la session.
@@ -0,0 +1,190 @@
1
+ # IAM Account
2
+
3
+ Version: `2.8.3`
4
+
5
+ Les APIs IAM Account servent à synchroniser des données utilisateur entre un backend SaaS et le backend IAM.
6
+
7
+ ## Règle
8
+
9
+ - Ces APIs sont **server-to-server uniquement**.
10
+ - N’appelez jamais ces méthodes depuis le frontend navigateur.
11
+ - Utilisez toujours `app_key` + `secret_key` côté backend.
12
+
13
+ ## API exposée
14
+
15
+ Le package exporte `iamAccountService` avec les méthodes suivantes:
16
+
17
+ - `linkPhone()`
18
+ - `linkEmail()`
19
+ - `refreshUserInfo()`
20
+ - `refreshUserInfoBulk()`
21
+ - `updateAvatar()`
22
+ - `resetAvatar()`
23
+
24
+ ## Références backend
25
+
26
+ - `POST /api/iam/link-phone`
27
+ - `POST /api/iam/link-email`
28
+ - `POST /api/iam/refresh-user-info`
29
+ - `POST /api/iam/update-avatar`
30
+ - `POST /api/iam/reset-avatar`
31
+
32
+ ## `linkEmail()`
33
+
34
+ Liaison d’un email à un utilisateur IAM existant.
35
+
36
+ ```ts
37
+ import { iamAccountService } from '@ollaid/native-sso';
38
+
39
+ const result = await iamAccountService.linkEmail({
40
+ app_key: 'oiam_ak_xxx',
41
+ secret_key: 'sk_xxx',
42
+ iam_reference: 'USR-XXXX',
43
+ email: 'john@example.com',
44
+ });
45
+ ```
46
+
47
+ Réponse:
48
+
49
+ ```json
50
+ {
51
+ "success": true,
52
+ "message": "Adresse email liée avec succès",
53
+ "user_reference": "USR-XXXX",
54
+ "user_infos": {
55
+ "name": "John Doe",
56
+ "email": "john@example.com",
57
+ "ccphone": "+221",
58
+ "phone": "771234567",
59
+ "address": "",
60
+ "town": "",
61
+ "country": "",
62
+ "image_url": "https://...",
63
+ "auth_2fa": false
64
+ }
65
+ }
66
+ ```
67
+
68
+ ## `linkPhone()`
69
+
70
+ Liaison d’un numéro de téléphone à un utilisateur IAM existant.
71
+
72
+ ```ts
73
+ await iamAccountService.linkPhone({
74
+ app_key: 'oiam_ak_xxx',
75
+ secret_key: 'sk_xxx',
76
+ iam_reference: 'USR-XXXX',
77
+ ccphone: '+221',
78
+ phone: '771234567',
79
+ });
80
+ ```
81
+
82
+ ## `refreshUserInfo()`
83
+
84
+ Récupère `user_infos` pour un alias donné.
85
+
86
+ ```ts
87
+ await iamAccountService.refreshUserInfo({
88
+ secret_key: 'sk_xxx',
89
+ alias_reference: 'ALI-XXXX',
90
+ });
91
+ ```
92
+
93
+ Réponse single:
94
+
95
+ ```json
96
+ {
97
+ "success": true,
98
+ "user_reference": "USR-XXXX",
99
+ "alias_reference": "ALI-XXXX",
100
+ "login_type": "email",
101
+ "user_infos": {
102
+ "name": "John Doe",
103
+ "email": "john@example.com",
104
+ "ccphone": "+221",
105
+ "phone": "771234567",
106
+ "address": "",
107
+ "town": "",
108
+ "country": "",
109
+ "image_url": "https://...",
110
+ "auth_2fa": false
111
+ }
112
+ }
113
+ ```
114
+
115
+ ## `refreshUserInfoBulk()`
116
+
117
+ Mode bulk pour synchroniser plusieurs `alias_reference`.
118
+
119
+ ```ts
120
+ await iamAccountService.refreshUserInfoBulk({
121
+ secret_key: 'sk_xxx',
122
+ alias_references: ['ALI-1', 'ALI-2'],
123
+ });
124
+ ```
125
+
126
+ Réponse:
127
+
128
+ ```json
129
+ {
130
+ "success": true,
131
+ "total_requested": 2,
132
+ "total_found": 1,
133
+ "total_errors": 1,
134
+ "data": [
135
+ {
136
+ "user_reference": "USR-XXXX",
137
+ "alias_reference": "ALI-1",
138
+ "login_type": "email",
139
+ "user_infos": {
140
+ "name": "John Doe",
141
+ "email": "john@example.com",
142
+ "ccphone": "+221",
143
+ "phone": "771234567",
144
+ "address": "",
145
+ "town": "",
146
+ "country": "",
147
+ "image_url": "https://...",
148
+ "auth_2fa": false
149
+ }
150
+ }
151
+ ],
152
+ "errors": [
153
+ {
154
+ "alias_reference": "ALI-2",
155
+ "error": "Alias non trouvé"
156
+ }
157
+ ]
158
+ }
159
+ ```
160
+
161
+ ## `updateAvatar()`
162
+
163
+ Met à jour l’avatar d’un utilisateur sur un `AppAccess` donné.
164
+
165
+ ```ts
166
+ await iamAccountService.updateAvatar({
167
+ app_key: 'oiam_ak_xxx',
168
+ secret_key: 'sk_xxx',
169
+ alias_reference: 'ALI-XXXX',
170
+ avatar_url: 'https://cdn.example.com/avatar.jpg',
171
+ });
172
+ ```
173
+
174
+ ## `resetAvatar()`
175
+
176
+ Réinitialise l’avatar spécifique à l’application pour revenir sur la cascade standard.
177
+
178
+ ```ts
179
+ await iamAccountService.resetAvatar({
180
+ app_key: 'oiam_ak_xxx',
181
+ secret_key: 'sk_xxx',
182
+ alias_reference: 'ALI-XXXX',
183
+ });
184
+ ```
185
+
186
+ ## Sécurité
187
+
188
+ - `secret_key` ne doit jamais être exposée au frontend.
189
+ - Ces appels doivent être exécutés uniquement par le backend SaaS.
190
+ - Ces méthodes ne remplacent pas le flux SSO principal.
@@ -0,0 +1,115 @@
1
+ # Advanced Services
2
+
3
+ Version: `2.8.3`
4
+
5
+ Ces helpers sont utiles pour les parcours IAM avancés, mais ils ne remplacent pas le flux SSO principal.
6
+
7
+ ## Règles
8
+
9
+ - Utilisez-les seulement après authentification.
10
+ - Ne les appelez pas depuis du code non authentifié.
11
+ - Ils s’appuient sur les credentials chargés par `nativeAuthService`.
12
+
13
+ ## `mobilePasswordService`
14
+
15
+ Flux de récupération de mot de passe mobile.
16
+
17
+ ### `initRecovery(email)`
18
+
19
+ ```ts
20
+ import { mobilePasswordService } from '@ollaid/native-sso';
21
+
22
+ const init = await mobilePasswordService.initRecovery('john@example.com');
23
+ ```
24
+
25
+ Réponse possible:
26
+
27
+ ```json
28
+ {
29
+ "success": true,
30
+ "process_token": "proc_xxx",
31
+ "status": "pending_otp",
32
+ "expires_at": "2026-05-15T18:00:00Z",
33
+ "otp_code_dev": "123456",
34
+ "receive_mode": "email",
35
+ "masked_email": "j***@example.com"
36
+ }
37
+ ```
38
+
39
+ ### `selectMethod(processToken, receiveChoice)`
40
+
41
+ Choisit `email` ou `phone`.
42
+
43
+ ### `reset(processToken, password)`
44
+
45
+ Termine la réinitialisation du mot de passe.
46
+
47
+ ### `resendOtp(processToken)`
48
+
49
+ Renvoie l’OTP et expose parfois un délai de cooldown.
50
+
51
+ ## `profileChangeService`
52
+
53
+ Flux OTP pour changer email ou téléphone.
54
+
55
+ ### `requestEmailChange(newEmail)`
56
+
57
+ Demande un changement d’email avec OTP.
58
+
59
+ ```ts
60
+ import { profileChangeService } from '@ollaid/native-sso';
61
+
62
+ const request = await profileChangeService.requestEmailChange('new@example.com');
63
+ ```
64
+
65
+ Réponse possible:
66
+
67
+ ```json
68
+ {
69
+ "success": true,
70
+ "message": "Demande envoyée",
71
+ "request_id": 42,
72
+ "method": "phone",
73
+ "otp_dev": "654321"
74
+ }
75
+ ```
76
+
77
+ ### `requestPhoneChange(ccphone, phone)`
78
+
79
+ Demande un changement de téléphone avec OTP.
80
+
81
+ ### `verifyOldOTP(requestId, otpCode)` / `verifyNewOTP(requestId, otpCode)`
82
+
83
+ Valide l’ancienne ou la nouvelle valeur selon le workflow.
84
+
85
+ ### `resendOTP(requestId)` / `switchMethod(requestId, method)`
86
+
87
+ Permet de renvoyer le code ou de basculer entre email et téléphone.
88
+
89
+ ## `profileMediaService`
90
+
91
+ Upload de l’image de profil.
92
+
93
+ ```ts
94
+ import { profileMediaService } from '@ollaid/native-sso';
95
+
96
+ await profileMediaService.uploadProfileImage(fileBlob, 'avatar.jpg');
97
+ ```
98
+
99
+ Réponse possible:
100
+
101
+ ```json
102
+ {
103
+ "message": "Image mise à jour",
104
+ "user_infos": {
105
+ "name": "John Doe",
106
+ "image_url": "https://cdn.example.com/avatar.jpg"
107
+ }
108
+ }
109
+ ```
110
+
111
+ ## Sécurité
112
+
113
+ - Ces helpers utilisent la session courante et le contexte device.
114
+ - Les changements de profil restent côté IAM.
115
+ - Si vous n’en avez pas besoin, vous pouvez les ignorer complètement.
@@ -0,0 +1,16 @@
1
+ # Migration Notes
2
+
3
+ Version: `2.8.3`
4
+
5
+ ## Version 2.8.2
6
+
7
+ - `@capacitor/device` est une peer dependency optionnelle.
8
+ - Le package chiffre les données persistées au repos.
9
+ - Le frontend SaaS récupère la session via `getSsoSessionSnapshot()`.
10
+ - `logout()` est le flux de sortie recommandé.
11
+
12
+ ## Côté intégrateurs
13
+
14
+ - Gardez les anciens helpers uniquement pour compatibilité.
15
+ - Ne lisez plus le storage directement dans le frontend hôte.
16
+ - Si vous êtes sur Capacitor, fournissez un storage natif sécurisé si vous en avez un.
@@ -0,0 +1,139 @@
1
+ # Update Infos
2
+
3
+ Version: `2.8.3`
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.3`
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.