Skip to content

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_token signé avec sa propre clé RSA privée et redirige vers A1Connect
  • A1Connect valide la signature avec la clé publique de la plateforme (iss permet 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 jti est consommé une seule fois (UsedJtis table) — le token ne peut pas être rejoué
  • Le logout utilise le même mécanisme (/deconnexion) mais A1Connect fait un window.open sans POST

Chantier : Gestion des comptes cross-plateformes

Statut : stable Périmètre : article1-connect

  • activeAccounts: String[] sur User — liste des plateformes où l'utilisateur a un compte actif
  • origin — 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)
  • obendyToken stocke le token tiers pour le lien de compte