@ollaid/native-sso 2.8.1 → 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,103 @@
1
+ # Login Redirect & Access Flow
2
+
3
+ Version: `2.8.3`
4
+
5
+ Ce document décrit le contrat à respecter pour éviter les deux régressions les plus fréquentes:
6
+
7
+ - rester bloqué sur la page SSO après une connexion réussie
8
+ - afficher une connexion réussie alors que l'utilisateur n'a pas encore d'accès à l'application SaaS
9
+
10
+ ## 1) Redirection après connexion
11
+
12
+ La page autonome `NativeSSOPage` doit être utilisée avec un `redirectAfterLogin` lorsque vous voulez renvoyer automatiquement l'utilisateur vers une page de votre SaaS après authentification.
13
+
14
+ Exemple:
15
+
16
+ ```tsx
17
+ <NativeSSOPage
18
+ saasApiUrl="https://your-saas.com/api"
19
+ iamApiUrl="https://identityam.ollaid.com/api"
20
+ redirectAfterLogin="https://your-saas.com/app"
21
+ redirectAfterLogout="https://your-saas.com/"
22
+ />
23
+ ```
24
+
25
+ Règle d'intégration:
26
+
27
+ - si `redirectAfterLogin` est fourni, la page SSO doit rediriger automatiquement dès qu'une session valide existe
28
+ - si `redirectAfterLogin` n'est pas fourni, la page peut rester sur l'écran SSO avec le bouton `Déconnexion`
29
+ - `onLoginSuccess` sert au callback métier du frontend, mais ne remplace pas la redirection
30
+
31
+ Comportement attendu côté page:
32
+
33
+ - connexion réussie -> callback `onLoginSuccess` puis redirection vers `redirectAfterLogin`
34
+ - session déjà valide au chargement -> redirection automatique vers `redirectAfterLogin`
35
+ - déconnexion -> redirection vers `redirectAfterLogout` si ce paramètre est fourni
36
+
37
+ ## 2) Contrôle d'accès par application
38
+
39
+ Le fait d'avoir un compte IAM ne signifie pas automatiquement que l'utilisateur a accès à chaque SaaS.
40
+
41
+ Flux attendu:
42
+
43
+ 1. l'utilisateur saisit son email ou son téléphone
44
+ 2. il passe l'authentification IAM
45
+ 3. le backend vérifie s'il possède déjà un `AppAccess` pour cette application
46
+ 4. si oui, il renvoie une connexion normale
47
+ 5. si non, il renvoie `needs_access`
48
+ 6. le package affiche alors le modal de confirmation d'accès
49
+ 7. l'utilisateur confirme
50
+ 8. seulement à ce moment-là la connexion peut être finalisée
51
+
52
+ Réponse attendue quand l'accès n'existe pas encore:
53
+
54
+ ```json
55
+ {
56
+ "success": true,
57
+ "needs_access": true,
58
+ "process_token": "pst_xxx",
59
+ "application": {
60
+ "id": 12,
61
+ "name": "Mon SaaS",
62
+ "logo": "https://..."
63
+ },
64
+ "user": {
65
+ "id": 42,
66
+ "name": "John Doe",
67
+ "email": "john@example.com"
68
+ }
69
+ }
70
+ ```
71
+
72
+ Règles backend:
73
+
74
+ - `needs_access` est le comportement par défaut quand l'accès à l'application n'existe pas encore
75
+ - `BYPASS=true` sur le SaaS permet au backend d'auto-créer l'accès et de finaliser la connexion sans afficher le modal
76
+ - le backend ne doit pas renvoyer une connexion finale tant que l'accès application n'a pas été validé, sauf si `BYPASS=true`
77
+
78
+ Règles frontend:
79
+
80
+ - si `needs_access` est reçu, afficher le modal de confirmation
81
+ - ne pas appeler le callback de redirection finale avant la confirmation
82
+ - ne pas marquer l'application comme connectée tant que `grantAccess()` n'a pas terminé
83
+
84
+ ## 3) Symptôme classique à éviter
85
+
86
+ Si l'utilisateur voit:
87
+
88
+ - `Connexion réussie`
89
+ - puis reste sur la page SSO avec `Déconnexion`
90
+
91
+ alors l'intégration n'a probablement pas fourni `redirectAfterLogin`, ou bien le callback frontend ne redirige pas vers une route métier.
92
+
93
+ Si l'utilisateur se connecte pour la première fois sur un SaaS où il n'a jamais eu d'accès, mais qu'il voit directement `Connexion réussie` sans modal `needs_access` alors que `BYPASS` est désactivé, le backend a probablement validé la session trop tôt.
94
+
95
+ ## 4) Contrat à respecter
96
+
97
+ Le package peut gérer l'UI, mais l'intégrateur SaaS doit fournir:
98
+
99
+ - `redirectAfterLogin`
100
+ - `redirectAfterLogout` si nécessaire
101
+ - un backend qui renvoie `needs_access` quand l'accès à l'application n'existe pas encore, sauf si `BYPASS=true`
102
+
103
+ Pour le détail du format des réponses backend, voir [Backend Contract](./2_backend-contract.md).
@@ -0,0 +1,130 @@
1
+ # Session Refresh & Resilience
2
+
3
+ Version: `2.8.3`
4
+
5
+ Ce document décrit le contrat de refresh token, la persistance des sessions et la stratégie de résilience attendue pour éviter toute déconnexion inutile en cas de réseau instable ou d'erreur transitoire.
6
+
7
+ ## Objectif
8
+
9
+ Le but est simple:
10
+
11
+ - ne pas perdre une session valide à cause d'une coupure réseau temporaire
12
+ - ne pas déconnecter un utilisateur si le backend est indisponible quelques secondes
13
+ - ne considérer une session comme invalide que sur une réponse explicite du backend
14
+
15
+ ## Contrat de session
16
+
17
+ Une session Native SSO peut contenir:
18
+
19
+ - `token` ou `auth_token`
20
+ - `expires_at`
21
+ - `refresh_token`
22
+ - `refresh_expires_at`
23
+ - `app_access_token_ref`
24
+ - `user`
25
+
26
+ Le package doit persister ces valeurs quand elles sont présentes.
27
+
28
+ ## Contrat du refresh token
29
+
30
+ Le refresh token n'est pas un simple cache local. Il doit être traité comme une référence de session côté SaaS.
31
+
32
+ Le backend SaaS doit:
33
+
34
+ - créer un `refresh_token` au moment de l'exchange
35
+ - persister son hash dans `personal_access_tokens.refresh_token_hash`
36
+ - persister sa date d'expiration dans `personal_access_tokens.refresh_expires_at`
37
+ - retourner le nouveau `refresh_token` à chaque rotation
38
+
39
+ Le package doit:
40
+
41
+ - stocker `refresh_token` et `refresh_expires_at` quand la réponse les fournit
42
+ - utiliser `POST /api/native/refresh` seulement si `refresh_token` existe
43
+ - remplacer le refresh token local si le backend le rote
44
+
45
+ ## Résultat attendu au refresh
46
+
47
+ Le backend peut répondre:
48
+
49
+ ```json
50
+ {
51
+ "success": true,
52
+ "expires_at": "2026-06-07T12:00:00+00:00",
53
+ "refresh_token": "rt_new_xxx",
54
+ "refresh_expires_at": "2026-08-07T12:00:00+00:00",
55
+ "app_access_token_ref": "aat_ref_xxx",
56
+ "user": {
57
+ "id": 42,
58
+ "name": "John Doe",
59
+ "email": "john@example.com"
60
+ }
61
+ }
62
+ ```
63
+
64
+ Dans ce cas:
65
+
66
+ - la session reste active
67
+ - le package met à jour le token local si nécessaire
68
+ - le refresh token local est remplacé par le nouveau
69
+
70
+ ## Réponses d'erreur
71
+
72
+ ### `invalid_refresh`
73
+
74
+ Cela signifie:
75
+
76
+ - le refresh token ne correspond à aucune session connue
77
+ - ou il a expiré
78
+ - ou le hash stocké en base n'a jamais été persisté
79
+ - ou le backend a déjà rotaté le refresh token et le client utilise une ancienne valeur
80
+
81
+ Ce cas doit être traité comme une fin de session réelle.
82
+
83
+ ### Erreur réseau / timeout / 5xx
84
+
85
+ Cela ne doit **pas** déconnecter immédiatement l'utilisateur.
86
+
87
+ Le bon comportement est:
88
+
89
+ - conserver la session locale
90
+ - garder le token actuel
91
+ - réessayer plus tard
92
+ - ne déconnecter que si le backend renvoie explicitement un échec définitif
93
+
94
+ ## Règle de résilience
95
+
96
+ Le package doit différencier:
97
+
98
+ - **échec transitoire**: réseau, timeout, 502, 503, 504
99
+ - **échec définitif**: `invalid_refresh`, `session_expired_idle`, token révoqué, réponse `401` explicite
100
+
101
+ Un échec transitoire ne doit pas vider le storage de session.
102
+
103
+ ## Recommandation côté frontend hôte
104
+
105
+ - ne pas appeler `logout()` automatiquement sur une simple erreur réseau
106
+ - ne pas supprimer le storage si `refresh()` échoue sur timeout
107
+ - afficher un état hors ligne ou un avertissement, mais garder l'utilisateur connecté
108
+ - ne forcer la déconnexion qu'après plusieurs échecs définitifs consécutifs ou une réponse `401` claire
109
+
110
+ ## Recommandation côté backend SaaS
111
+
112
+ - persister `refresh_token_hash` au moment de l'exchange
113
+ - mettre à jour `refresh_expires_at` à chaque rotation
114
+ - ne pas réutiliser un ancien refresh token après rotation
115
+ - renvoyer toujours le nouveau `refresh_token` si une rotation est faite
116
+
117
+ ## Dépannage
118
+
119
+ Si vous voyez `Refresh token invalide ou expiré`:
120
+
121
+ - vérifiez que la table `personal_access_tokens` contient bien `refresh_token_hash`
122
+ - vérifiez que le hash correspond au `refresh_token` stocké côté package
123
+ - vérifiez que `refresh_expires_at` est encore valide
124
+ - vérifiez que le frontend n'utilise pas un token d'une ancienne session
125
+
126
+ Si vous voyez des déconnexions pendant une panne réseau:
127
+
128
+ - vérifiez que votre code n'efface pas la session sur un simple timeout
129
+ - vérifiez que votre logique de refresh ne confond pas `network error` et `invalid_refresh`
130
+
@@ -0,0 +1,55 @@
1
+ # Needs Access & Bypass
2
+
3
+ Version: `2.8.3`
4
+
5
+ Ce document décrit le comportement attendu quand un utilisateur est authentifié mais n'a pas encore d'accès à une application SaaS.
6
+
7
+ ## Règle par défaut
8
+
9
+ Le comportement par défaut doit être `needs_access`.
10
+
11
+ Cela signifie:
12
+
13
+ - si l'utilisateur a bien terminé son authentification IAM mais ne possède pas encore d'accès à l'application ciblée, le backend doit renvoyer `needs_access`
14
+ - le package affiche alors le modal de confirmation
15
+ - la connexion n'est finalisée qu'après `grantAccess()`
16
+
17
+ ## `BYPASS=true`
18
+
19
+ Le SaaS peut activer un mode de bypass via son `.env`:
20
+
21
+ ```env
22
+ BYPASS=true
23
+ ```
24
+
25
+ Dans ce cas:
26
+
27
+ - si l'utilisateur n'a pas encore d'accès à l'application, le backend auto-crée cet accès
28
+ - le modal `needs_access` n'est pas affiché
29
+ - la connexion est finalisée immédiatement comme si l'utilisateur avait confirmé manuellement
30
+
31
+ ## Valeurs possibles
32
+
33
+ - `BYPASS=true` -> auto-acceptation du flux `needs_access`
34
+ - `BYPASS=false` -> modal `needs_access` obligatoire si l'accès manque
35
+ - `BYPASS` absent -> comportement identique à `false`
36
+ - `BYPASS=null` ou vide -> comportement identique à `false`
37
+
38
+ ## Ce que le frontend doit faire
39
+
40
+ - si `needs_access` est reçu, afficher le modal de confirmation
41
+ - si la connexion a déjà été finalisée, rediriger vers la page métier prévue
42
+ - ne pas considérer l'utilisateur comme connecté tant que `grantAccess()` n'a pas terminé
43
+
44
+ ## Ce que le backend doit faire
45
+
46
+ - vérifier si l'utilisateur a déjà un `AppAccess`
47
+ - si oui, finaliser la connexion normalement
48
+ - si non et `BYPASS` est désactivé, renvoyer `needs_access`
49
+ - si non et `BYPASS=true`, créer l'accès et finaliser la connexion
50
+
51
+ ## Résumé
52
+
53
+ - `needs_access` = comportement standard
54
+ - `BYPASS=true` = auto-acceptation contrôlée par le SaaS
55
+ - le flux natif ne doit plus dépendre d'un mode legacy pour le comportement courant
@@ -0,0 +1,60 @@
1
+ # Must Have Working
2
+
3
+ Version: `2.8.3`
4
+
5
+ Cette page sert de vérification finale. Si un de ces points manque, l'intégration Native SSO n'est pas considérée comme complète.
6
+
7
+ ## 1) Authentification de base
8
+
9
+ - **Connexion email**: l'utilisateur peut se connecter avec son email IAM.
10
+ - **Connexion téléphone**: l'utilisateur peut se connecter avec son numéro IAM.
11
+ - **OTP**: le code de vérification fonctionne quand le flux OTP est utilisé.
12
+ - **Mot de passe**: la validation du mot de passe IAM fonctionne sans bloquer sur un faux état d'accès.
13
+
14
+ ## 2) Accès application
15
+
16
+ - **`needs_access`**: état renvoyé quand l'utilisateur est authentifié mais n'a pas encore d'accès à l'application SaaS.
17
+ - **Modal d'accès**: l'application affiche la demande de confirmation quand `needs_access` est reçu.
18
+ - **Création d'accès**: après validation manuelle, l'accès SaaS est créé et la connexion se termine.
19
+ - **`BYPASS=true`**: le SaaS peut auto-valider l'accès manquant et finaliser la connexion sans afficher le modal.
20
+
21
+ ## 3) Redirection
22
+
23
+ - **`redirectAfterLogin`**: la page SSO redirige automatiquement après connexion réussie.
24
+ - **`redirectAfterLogout`**: la page SSO redirige vers la page prévue après déconnexion.
25
+ - **Pas de page bloquée**: l'utilisateur ne doit pas rester sur la page SSO avec `Déconnexion` après une connexion réussie.
26
+
27
+ ## 4) Session et refresh
28
+
29
+ - **Refresh token**: la session peut être prolongée sans déconnexion inutile.
30
+ - **Résilience réseau**: une erreur réseau ne doit pas supprimer une session valide.
31
+ - **`invalid_refresh`**: ce statut signifie une vraie invalidation du refresh token, pas un simple timeout réseau.
32
+
33
+ ## 5) Sécurité et cohérence
34
+
35
+ - **Clés application**: `app_key` et `secret_key` doivent correspondre à l'application ciblée.
36
+ - **`encrypted_credentials`**: le blob chiffré doit suivre le contrat documenté dans `backend-contract.md`.
37
+ - **Signature HMAC**: si l'intégration l'utilise, la signature doit être valide pour les requêtes signées.
38
+
39
+ ## 6) Profil SSO
40
+
41
+ - **Onboarding profil**: les infos profil manquantes peuvent être complétées via la modal dédiée.
42
+ - **Synchronisation profil**: les changements de profil doivent remonter correctement entre IAM et SaaS.
43
+
44
+ ## 7) Règle de validation finale
45
+
46
+ Une intégration est considérée comme prête si:
47
+
48
+ 1. la connexion se fait
49
+ 2. `needs_access` apparaît quand il manque un accès
50
+ 3. `BYPASS=true` fonctionne quand il est activé
51
+ 4. la redirection se fait vers la page métier attendue
52
+ 5. la session survit aux erreurs réseau
53
+ 6. les infos profil liées au SSO sont cohérentes
54
+
55
+ ## Résumé
56
+
57
+ - si le flux `needs_access` marche, la partie contrôle d'accès est bonne
58
+ - si `BYPASS=true` fonctionne, le SaaS peut automatiser l'onboarding d'accès
59
+ - si la session survit au réseau, le refresh token est correctement géré
60
+ - si la redirection est propre, l'expérience utilisateur est complète
@@ -0,0 +1,50 @@
1
+ # Backend Env Key
2
+
3
+ Version: `2.8.3`
4
+
5
+ Ce document liste les variables d'environnement backend nécessaires pour faire fonctionner le flux Native SSO.
6
+
7
+ ## Exemple de configuration
8
+
9
+ ```env
10
+ # ==============================================================================
11
+ # IAM SSO CONFIGURATION
12
+ # ==============================================================================
13
+ IAM_API_URL=https://identityam.ollaid.com/api
14
+ IAM_AUTH_URL=https://iam.ollaid.com
15
+
16
+ IAM_APP_KEY=oiam_ak_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
17
+ IAM_PUBLIC_KEY=oiam_pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
18
+ IAM_SECRET_KEY=oiam_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
19
+ IAM_WEBHOOK_SECRET=oiam_whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
20
+ IAM_DEBUG=false
21
+ BYPASS=false
22
+
23
+ # ==============================================================================
24
+ # END IAM SSO CONFIGURATION
25
+ # ==============================================================================
26
+ ```
27
+
28
+ ## Rôle des variables
29
+
30
+ - `IAM_API_URL`: URL API du backend IAM
31
+ - `IAM_AUTH_URL`: URL d'authentification IAM utilisée par le backend ou les redirections
32
+ - `IAM_APP_KEY`: clé publique d'application utilisée pour identifier le SaaS
33
+ - `IAM_PUBLIC_KEY`: clé publique de chiffrement / vérification selon votre implémentation
34
+ - `IAM_SECRET_KEY`: clé secrète utilisée pour les signatures et validations serveur
35
+ - `IAM_WEBHOOK_SECRET`: secret utilisé pour signer les webhooks
36
+ - `IAM_DEBUG`: active ou désactive les logs de debug IAM
37
+ - `BYPASS`: active l'auto-acceptation du flux `needs_access` quand il vaut `true`
38
+
39
+ ## Règles
40
+
41
+ - ne jamais committer les vraies clés dans la documentation
42
+ - utiliser les vraies valeurs uniquement dans le `.env` du projet consommateur
43
+ - `BYPASS=false` ou absent conserve le modal `needs_access`
44
+ - `BYPASS=true` auto-crée l'accès quand il manque
45
+
46
+ ## Résumé
47
+
48
+ - ce fichier sert de modèle
49
+ - les valeurs sensibles doivent rester privées
50
+ - `BYPASS` est la seule variable ici qui modifie directement le comportement du flux d'accès
@@ -0,0 +1,87 @@
1
+ # Quick Start
2
+
3
+ Version: `2.8.3`
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @ollaid/native-sso
9
+ ```
10
+
11
+ ## Route de test
12
+
13
+ ```tsx
14
+ import { NativeSSOPage } from '@ollaid/native-sso';
15
+
16
+ <Route
17
+ path="/auth/native-sso"
18
+ element={
19
+ <NativeSSOPage
20
+ saasApiUrl="https://your-saas.com/api"
21
+ iamApiUrl="https://identityam.ollaid.com/api"
22
+ homeUrl="https://your-saas.com/"
23
+ onLoginSuccess={(token, user) => {
24
+ console.log(token, user);
25
+ }}
26
+ />
27
+ }
28
+ />
29
+ ```
30
+
31
+ `homeUrl` est optionnel. S'il est fourni, le package affiche un lien "Retourner à l'accueil" sur les écrans racine de connexion et d'inscription. Sans valeur, le lien reste caché.
32
+
33
+ Si vous utilisez `NativeSSOPage`, fournissez aussi `redirectAfterLogin` pour éviter de rester sur la page SSO après une connexion réussie:
34
+
35
+ ```tsx
36
+ <NativeSSOPage
37
+ saasApiUrl="https://your-saas.com/api"
38
+ iamApiUrl="https://identityam.ollaid.com/api"
39
+ redirectAfterLogin="https://your-saas.com/app"
40
+ />
41
+ ```
42
+
43
+ Sans `redirectAfterLogin`, la page peut rester affichée avec le bouton `Déconnexion`, ce qui est normal pour un usage purement autonome.
44
+
45
+ ## Côté frontend SaaS
46
+
47
+ ```ts
48
+ import { getSsoSessionSnapshot, logout } from '@ollaid/native-sso';
49
+
50
+ const session = getSsoSessionSnapshot();
51
+
52
+ if (session.authToken) {
53
+ console.log(session.user);
54
+ }
55
+ ```
56
+
57
+ ### Forme du snapshot
58
+
59
+ ```ts
60
+ {
61
+ authToken: string | null;
62
+ refreshToken: string | null;
63
+ refreshExpiresAt: string | null;
64
+ tokenExpiresAt: string | null;
65
+ appAccessTokenRef: string | null;
66
+ aliasReference: string | null;
67
+ accountType: string | null;
68
+ deviceId: string | null;
69
+ sessionUuid: string | null;
70
+ user: unknown | null;
71
+ }
72
+ ```
73
+
74
+ ### Déconnexion
75
+
76
+ ```ts
77
+ const result = await logout();
78
+ // { success: true }
79
+ ```
80
+
81
+ ## Règles simples
82
+
83
+ - N’accédez pas au storage directement.
84
+ - Utilisez `getSsoSessionSnapshot()` pour lire la session.
85
+ - Utilisez `logout()` pour déconnecter.
86
+ - `clearAuthToken()` reste seulement pour compatibilité.
87
+ - Si vous êtes sur Capacitor, fournissez un `storage` natif si possible.
@@ -0,0 +1,199 @@
1
+ # Backend Contract
2
+
3
+ Version: `2.8.3`
4
+
5
+ ## Endpoints SaaS requis
6
+
7
+ - `GET /api/native/config`
8
+ - `POST /api/native/exchange`
9
+ - `POST /api/native/check-token`
10
+ - `POST /api/native/refresh`
11
+ - `POST /api/native/logout`
12
+ - `POST /api/native/password-link` pour le flux de redirection mot de passe
13
+
14
+ ## Exemple rapide
15
+
16
+ ### 1) `GET /api/native/config`
17
+
18
+ Réponse:
19
+
20
+ ```json
21
+ {
22
+ "success": true,
23
+ "app_key": "oiam_ak_xxx",
24
+ "encrypted_credentials": "base64(IV::ciphertext_base64)...",
25
+ "iam_api_url": "https://identityam.ollaid.com/api",
26
+ "credentials_ttl": 300,
27
+ "debug": false
28
+ }
29
+ ```
30
+
31
+ Format attendu pour `encrypted_credentials`:
32
+
33
+ - chiffrement `AES-256-CBC`
34
+ - IV aléatoire de 16 octets
35
+ - clé dérivée avec `hash('sha256', secret_key, true)`
36
+ - sérialisation finale: `base64(IV::ciphertext_base64)`
37
+ - contenu chiffré: un JSON avec au minimum `app_key` et `secret_key`
38
+
39
+ ### 2) `POST /api/native/exchange`
40
+
41
+ Body:
42
+
43
+ ```json
44
+ {
45
+ "callback_token": "cbk_xxx"
46
+ }
47
+ ```
48
+
49
+ Réponse:
50
+
51
+ ```json
52
+ {
53
+ "success": true,
54
+ "token": "1|sanctum_token",
55
+ "expires_at": "2026-05-15T17:00:00+00:00",
56
+ "refresh_token": "ref_xxx",
57
+ "refresh_expires_at": "2026-06-15T17:00:00+00:00",
58
+ "app_access_token_ref": "aat_ref_xxx",
59
+ "user": {
60
+ "reference": "USR-XXXX",
61
+ "name": "John Doe",
62
+ "email": "john@example.com",
63
+ "alias_reference": "ALI-XXXX"
64
+ },
65
+ "user_infos": {
66
+ "name": "John Doe",
67
+ "email": "john@example.com",
68
+ "ccphone": "+221",
69
+ "phone": "771234567",
70
+ "address": "Rue 10, Plateau",
71
+ "town": "Dakar",
72
+ "country": "SN"
73
+ }
74
+ }
75
+ ```
76
+
77
+ ### 2 bis) Flux `needs_access` sur une nouvelle application
78
+
79
+ Quand l'utilisateur est bien authentifié côté IAM mais qu'il n'a encore aucun accès sur l'application SaaS ciblée, le backend doit renvoyer un état `needs_access`.
80
+
81
+ Réponse attendue:
82
+
83
+ ```json
84
+ {
85
+ "success": true,
86
+ "needs_access": true,
87
+ "process_token": "pst_xxx",
88
+ "application": {
89
+ "id": 12,
90
+ "name": "Mon SaaS",
91
+ "logo": "https://..."
92
+ },
93
+ "user": {
94
+ "id": 42,
95
+ "name": "John Doe",
96
+ "email": "john@example.com"
97
+ }
98
+ }
99
+ ```
100
+
101
+ Comportement du package:
102
+
103
+ - il affiche l'écran de confirmation "Confirmer la création de mon compte"
104
+ - il garde le `process_token` pour finaliser l'attribution d'accès
105
+ - il ne termine pas la connexion tant que l'utilisateur n'a pas confirmé
106
+
107
+ Cas de configuration backend:
108
+
109
+ - `BYPASS=true` sur le SaaS -> le backend auto-crée l'accès et finalise la connexion comme si le modal avait été validé
110
+ - `BYPASS` absent, `false` ou vide -> le backend renvoie `needs_access` dès que l'accès n'existe pas encore
111
+
112
+ Le flux natif ne dépend plus du mode legacy d'autorisation pour le comportement standard. Le contrat natif doit être centré sur `needs_access` par défaut.
113
+
114
+ ### 3) `POST /api/native/check-token`
115
+
116
+ Réponse `200` si le token est valide:
117
+
118
+ ```json
119
+ {
120
+ "success": true,
121
+ "user": {
122
+ "name": "John Doe",
123
+ "email": "john@example.com"
124
+ }
125
+ }
126
+ ```
127
+
128
+ ### 4) `POST /api/native/refresh`
129
+
130
+ Le package tente ce refresh si `refresh_token` existe et que `check-token` renvoie `401`.
131
+
132
+ ### 5) `POST /api/native/logout`
133
+
134
+ Réponse:
135
+
136
+ ```json
137
+ { "success": true }
138
+ ```
139
+
140
+ ### 6) `POST /api/native/password-link`
141
+
142
+ Réponse recommandée:
143
+
144
+ ```json
145
+ {
146
+ "success": true,
147
+ "redirect_url": "https://identityam.ollaid.com/auth/auto-connect?magic_token=xxx",
148
+ "magic_token": "xxx",
149
+ "expires_at": "2026-05-18T12:00:00+00:00"
150
+ }
151
+ ```
152
+
153
+ Le backend SaaS doit relayer l'appel vers IAM sur `POST /api/backoffice/auth/password-link`, puis renvoyer au package une URL de redirection prête à ouvrir.
154
+
155
+ ## Ce que le package envoie
156
+
157
+ - `X-Device-Id`
158
+ - `X-Session-UUID`
159
+ - `X-Device-Name`
160
+ - `X-Device-Model`
161
+ - `X-Device-Manufacturer`
162
+ - `X-Device-Operating-System`
163
+ - `X-Device-Os-Version`
164
+ - `X-Device-Platform`
165
+ - `X-Device-Browser`
166
+ - `X-Device-Language`
167
+ - `X-Device-WebView-Version`
168
+ - `X-Device-Is-Native`
169
+ - `X-Device-Label`
170
+
171
+ ## Ce que le backend doit faire
172
+
173
+ - Répondre en JSON.
174
+ - Gérer `refresh_token` si disponible.
175
+ - Retourner `refresh_token` / `refresh_expires_at` si vous activez le refresh.
176
+ - Faire du `check-token` un signal de validité, avec tentative de refresh côté package après un `401`.
177
+ - Révoquer de façon ciblée via `app_access_token_ref`.
178
+ - Mettre à jour `last_active_at` si vous appliquez la politique d’activité.
179
+ - Exposer les sessions sous forme multi-device sans tuer les autres appareils.
180
+ - Renvoyer aussi les champs de profil utiles au package dans `user_infos` quand ils sont connus, notamment `name`, `email`, `ccphone`, `phone`, `address`, `town` et `country`.
181
+
182
+ ## Dépannage
183
+
184
+ - `Identifiants application invalides` au moment du login natif signifie en pratique que `POST /api/iam/native/encrypt` n'a pas réussi à valider le couple `app_key` + `encrypted_credentials`.
185
+ - `Déchiffrement échoué` côté IAM veut dire que `encrypted_credentials` ne correspond pas à la `secret_key` attendue par l'application, ou que la valeur envoyée est mal formée.
186
+ - `Credentials expirés` veut dire que le blob de credentials reçu par le package est trop ancien par rapport au TTL configuré. Dans ce cas, le package doit refaire `GET /api/native/config`.
187
+ - Si l'utilisateur est authentifié mais n'a pas encore accès à l'application, le bon signal est `needs_access`. Le package doit afficher la demande de confirmation d'accès, pas un simple échec de login.
188
+ - Si ce flux n'apparaît plus, vérifiez que `BYPASS` n'est pas activé par erreur côté SaaS et que les endpoints IAM retournent bien `needs_access` après l'authentification.
189
+ - Si vous avez copié des clés depuis une application déjà en production et que le login échoue quand même, vérifiez d'abord que `GET /api/native/config` renvoie bien les clés attendues pour cette application.
190
+ - Vérifiez aussi que le SaaS ne sert pas un `encrypted_credentials` mis en cache avec une ancienne `secret_key`.
191
+ - Vérifiez enfin que le format chiffré reste `base64(IV::ciphertext_base64)` comme attendu par le package.
192
+
193
+ ## APIs IAM Account
194
+
195
+ Si votre intégration utilise aussi les synchronisations utilisateur server-to-server, consultez [IAM Account](./5_iam-account.md).
196
+
197
+ ## Session refresh et résilience
198
+
199
+ Le contrat de `refresh_token`, la rotation, la persistance des sessions et la gestion des erreurs réseau sont détaillés dans [Session Refresh & Resilience](./11_session-refresh-resilience.md).