Documentation VegLab Help

Intégration SSO — récupération des droits géographiques

Cette page décrit précisément comment VegLab récupère les droits spatiaux d'un utilisateur connecté via une source externe (Lobelia), et comment l'appel à l'API de la source est authentifié.

Principe

Le token OIDC reçu par l'API VegLab ne contient pas les droits : il porte seulement une référence, à savoir l' URL d'un endpoint de la source (façon OIDC Distributed Claims). VegLab appelle cet endpoint une fois par connexion pour récupérer les contraintes spatiales (communes, départements, etc.) et les persiste dans l'entité UserRight.

L'appel à l'endpoint de la source est authentifié par le token que la source a elle-même émis lors de l'authentification. La source valide donc ses propres tokens (aucune logique de validation spécifique à écrire de son côté) et en déduit l'utilisateur de façon prouvée — il n'y a ni secret partagé, ni e-mail déclaratif dans cet appel.

Le point clé : où VegLab trouve-t-il le token de la source ?

VegLab ne reçoit jamais le token de la source : le front se connecte à Keycloak et reçoit un token Keycloak (audience veglab-web). C'est Keycloak qui, en tant que broker OIDC, a obtenu et stocké le token de la source pendant le login (option storeToken de l'IdP).

VegLab récupère ce token stocké via l' endpoint broker token de Keycloak, en présentant le token Keycloak de l'utilisateur (celui qu'il a déjà en main, le Bearer de la requête entrante) :

GET http://keycloak:8080/realms/{realm}/broker/{alias}/token Authorization: Bearer <token Keycloak de l'utilisateur> → 200 { "access_token": "<token de la source>", "token_type": "Bearer", ... }

Où {alias} est l'alias de l'IdP Keycloak (configuré côté VegLab dans le claim_map, pas codé en dur). Keycloak vérifie le token Keycloak, contrôle que l'utilisateur possède le rôle client broker → read-token, rafraîchit le token de la source si nécessaire, puis le restitue.

Séquence complète d'un login

Front VegLabAPI VegLab(UserRightSynchronizer)Keycloak(broker)Source (Lobelia)Le token Keycloak porte le claim de référenceveglab_spatial_right_identification.endpointRequête API + Authorization: Bearer <token KC>GET /realms/{realm}/broker/{alias}/tokenAuthorization: Bearer <token KC>200 { access_token: <token source> }GET <endpoint du claim>Authorization: Bearer <token source>200 { constraints: [{ level, levelIds, cursor }] }Persiste UserRight (once-per-login, clé = sid)Front VegLabAPI VegLab(UserRightSynchronizer)Keycloak(broker)Source (Lobelia)

Garanties et garde-fous (côté VegLab)

  • Once-per-login: la synchronisation s'exécute une seule fois par session, repérée via le claim sid (UserRightSynchronizer::synchronize()), bien que le firewall soit stateless.

  • Allowlist anti-SSRF: l'URL extraite du claim n'est appelée que si son hôte figure dans VEGLAB_USER_RIGHTS_ALLOWED_HOSTS — variable par environnement, injectée par compose depuis le .env.<env> actif (détail dans docs/PERMISSIONS.md). Un hôte manquant se traduit par un refus d'authentification, pas par une session sans droits.

  • Fail-closed: si le token de la source ne peut être récupéré (rôle read-token manquant, absence de token stocké, endpoint injoignable…), l'authentification est refusée — on ne laisse pas entrer un utilisateur sans les droits qu'on est censé faire respecter.

  • Découplage: aucun nom de système externe n'apparaît dans le code ; l'alias du broker et la correspondance claim → (type, role) sont en configuration (config/services.yaml, app.user_rights.claim_map).

Prérequis de configuration Keycloak (realm veglab)

Sur l'IdP source (alias lobelia) :

  • storeToken: true — sans quoi Keycloak jette le token de la source et l'endpoint broker token ne renvoie rien.

  • addReadTokenRoleOnCreate: true — accorde le rôle broker/read-token aux nouveaux utilisateurs brokerés. Les utilisateurs déjà existants doivent se voir attribuer ce rôle manuellement.

  • Le scope du token émis par la source doit autoriser l'endpoint des droits.

Les deux mappers, à ne pas confondre

Le claim traverse Keycloak en deux étapes, portées par deux objets distincts que la console d'administration nomme presque pareil. C'est le second qui manque, en général, quand le claim n'apparaît pas dans le token alors que « le mapper est bien là ».

Objet

Où il se configure

Ce qu'il fait

Identity Provider Mapper (Attribute Importer)

Identity providers → lobelia → Mappers

claim de la source → attribut utilisateur veglab_spatial_endpoint

Protocol mapper (User Attribute)

Clients → veglab-web → Client scopes → veglab-web-dedicated

attribut utilisateur → claim veglab_spatial_right_identification.endpoint de l'access token

Le premier ne touche pas au token que reçoit VegLab : il remplit un attribut sur le compte Keycloak. Le second est celui qui écrit le claim. Valeurs exactes des champs : checklist d'installation (INSTALL.md, section 8).

Deux effets retardés à connaître : le syncMode: FORCE de l'importer ne réimporte l'attribut qu'au prochain login brokeré, et Keycloak lit read-token dans le token présenté, pas en base — dans les deux cas, il faut se reconnecter pour observer le changement.

Dépannage

Le symptôme désigne le maillon, parce que les deux moitiés de la chaîne échouent de façon opposée.

Claim absent du token. Le synchronizer ne trouve rien à synchroniser, ne logue rien, et l'utilisateur entre sans aucune contrainte: faute de UserRight, le voter considère que la restriction ne le concerne pas et autorise tout. Un droit spatial qui « ne marche pas » est donc toujours plus permissif, jamais plus restrictif. Sa signature en base :

SELECT u.email, u.rights_synced_sid, r.type, r.role, r.constraints, r.last_synced_at FROM vl_user u LEFT JOIN vl_user_right r ON r.owner_id = u.id WHERE u.email = '<email>';

rights_synced_sid renseigné et aucune ligne vl_user_right: la synchronisation est allée au bout sans jamais voir le claim (le sid n'est écrit qu'en toute fin de parcours, et seulement si rien n'a échoué).

Tout le reste est fail-closed: 401, « Nous n'avons pas pu récupérer vos droits dans le SI source ». Le log applicatif (docker logs vl-api, filtré sur UserRight) dit lequel :

Message

Maillon en cause

Unusable endpoint for claim …

l'hôte de l'URL du claim n'est pas dans VEGLAB_USER_RIGHTS_ALLOWED_HOSTS

Broker token retrieval failed … status 403

le rôle broker/read-token manque sur le compte

Broker token retrieval failed … status 400

issuer : Keycloak n'a pas reconstruit son URL publique (KC_PROXY=edge et en-têtes Host/X-Forwarded-*)

Fetch failed for spatial/allow_identification …

la source a répondu hors 2xx

Last modified: 31 August 2026