SSO
A1Connect est le seul IDP. Les plateformes sont des SP purs : elles n'ont pas de session propre, elles délèguent tout à A1Connect via JWT RS256.
Chantier : Flux d'authentification
Statut : stable Périmètre : transverse
- La plateforme génère un
auth_req_tokensigné avec sa propre clé RSA privée et redirige vers A1Connect - A1Connect valide la signature avec la clé publique de la plateforme (
isspermet de sélectionner la bonne clé) - Après auth, A1Connect signe un JWT retour avec sa propre clé RSA privée et POST vers
redirect_uri - Chaque
jtiest consommé une seule fois (UsedJtistable) — le token ne peut pas être rejoué - Le logout utilise le même mécanisme (
/deconnexion) mais A1Connect fait unwindow.opensans POST
Chantier : Gestion des comptes cross-plateformes
Statut : stable Périmètre : article1-connect
activeAccounts: String[]surUser— liste des plateformes où l'utilisateur a un compte actiforigin— plateforme d'inscription initiale (immuable)- Quand une plateforme reçoit un premier login (
user___logged), elle crée son propre profil local lié à l'email A1Connect - La suppression d'un compte passe par
user___deleted: A1Connect orchestre, les plateformes nettoient leur côté
Flow browser détaillé (Inspire ↔ A1Connect)
Vue d'ensemble
Browser Inspire Front Inspire API A1Connect (A1C)
| | | |
|--[1] Accès route | | |
| protégée -----> | | |
| |--[2] GET | |
| | /auth-jwt/ | |
| | sso-jwt?url= | |
| | {targetUrl}-> | |
| |<--[3] auth_req_jwt (JWT RS256) |
| | |
|<--[4] redirect vers A1C/orientation |
| ?auth_req_jwt={token} |
| |
|--[5] Login/password sur A1C ---------------------------> |
|<--[6] POST vers Inspire API /auth-jwt/sso-redirect -------|
| body: { profile_jwt: "..." } |
| | | |
| | [7] verifyAsync(profile_jwt) |
| | findOrCreate user |
| | login(user) → token Inspire |
| | | |
|<--[8] redirect vers Front/auth/callback |
| ?loginToken={token}&isSignUp={bool} |
| | |
|--[9] AuthCallback.vue |
| auth.login(token) → cookie "inspire-v2" |
| → redirect vers home/questionnaire |
Étapes clés
[2–3] Génération du auth_req_jwt — le router guard Vue détecte une route protégée sans user. Le front appelle GET /auth-jwt/sso-jwt. L'API génère un JWT RS256 signé avec sa clé privée contenant redirect_uri (qui pointe vers POST /auth-jwt/sso-redirect) et un jti unique.
[6–7] Retour d'A1C — après login, A1C fait lui-même un POST vers redirect_uri avec un profile_jwt signé par la clé privée d'A1C. L'API vérifie avec SSO_PUBLIC_KEY, fait un findOrCreate en base, et génère un token Inspire valable 1 jour.
[8–9] Cookie + routing — l'API redirige vers /auth/callback?loginToken=.... Le front pose un cookie inspire-v2 (30 jours, SameSite=Lax), injecte le Bearer dans Axios, puis route selon le rôle (admin, lyceen, eclaireur…).
Pourquoi c'est impossible en curl/Bruno
| Étape | Pourquoi curl ne suffit pas |
|---|---|
| [5] Login sur A1C | Session interne opaque à A1C |
| [6] POST d'A1C vers l'API | C'est A1C qui fait ce POST, signé avec sa clé privée — on ne peut pas le forger |
Workaround actuel : POST /auth-jwt/login (email + password direct) — utilisé par Bruno. Ne fonctionne que pour les users avec un password local (pas les comptes SSO-only).
Options d'amélioration
| Option | Complexité | Description |
|---|---|---|
| Endpoint dev-login | Faible | POST /auth-jwt/dev-login?ssoId=xxx désactivé en prod par env var |
| Service account A1C | Moyenne | A1C expose un endpoint server-to-server avec client credentials |
| OAuth2 PKCE | Forte | Refonte d'A1C en IDP OAuth2 standard — Postman/Bruno le supportent nativement |
| SAML | Forte | OAuth2 PKCE (le plus propre long-terme) |
Chantier : Intégration Obendy
Statut : stable Périmètre : article1-connect
partnership: 'obendy'→ le mot de passe est remplacé par un UUID généré (l'utilisateur ne se connecte pas par password)obendyTokenstocke le token tiers pour le lien de compte