Aller au contenu

Configurer la connexion OIDC / SSO

Repod prend en charge l'authentification unique (Single Sign-On) via tout fournisseur OpenID Connect conforme aux standards — Keycloak, Authentik, Zitadel, Azure AD (Entra ID), Okta et ADFS ont tous été utilisés avec. Une fois configuré, les utilisateurs s'authentifient auprès de votre IdP et ne saisissent plus jamais de mot de passe local à Repod.

Repod implémente le flux Authorization Code avec PKCE (RFC 7636), le flux recommandé pour les SPA côté navigateur — aucun secret client n'est exposé au navigateur à aucun moment ; PKCE le remplace.


1. Prérequis

  • Un accès admin à Repod pour Paramètres → SSO.
  • Une application OIDC enregistrée dans votre IdP, avec :
  • Une URI de redirection pointant vers https://<votre-hôte-repod>/oidc-callback
  • Un identifiant client et un secret client
  • Les scopes openid email profile activés (par défaut)
  • L'URL de découverte de votre IdP — le document standard /.well-known/openid-configuration. Exemples :
  • Keycloak : https://sso.example.com/realms/myorg/.well-known/openid-configuration
  • Azure AD : https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration
  • Okta : https://<org>.okta.com/.well-known/openid-configuration

Fonctionnalité Enterprise

OIDC est verrouillé derrière license_svc.check_feature("oidc") — il nécessite une licence Enterprise (on-premise) ou un plan incluant le SSO (SaaS). Sur les installations Community/non licenciées, GET /api/v1/auth/oidc/public-config renvoie toujours {"enabled": false}, quel que soit ce qui est enregistré dans les paramètres.


2. Étape 1 — Enregistrer l'URI de redirection dans votre IdP

Avant de toucher à Repod, créez l'application cliente OIDC dans votre IdP et définissez son URI de redirection autorisée sur :

https://<votre-hôte-repod>/oidc-callback

Il s'agit d'une route frontend fixe (OidcCallbackPage.js) — elle n'est pas configurable par fournisseur au-delà de l'hôte lui-même. Si Repod est accessible via plusieurs noms d'hôte, enregistrez chacun comme URI de redirection autorisée distincte dans l'IdP.


3. Étape 2 — Ouvrir les paramètres SSO dans Repod

  1. Connectez-vous à Repod en tant qu'administrateur.
  2. Naviguez vers Paramètres → SSO.
  3. Activez le bouton Activer le SSO. Le reste du formulaire devient modifiable.

4. Étape 3 — Configurer la connexion

Champ Clé de paramètre Description
Nom du fournisseur provider_name Libellé affiché sur le bouton « Se connecter avec… » sur la page de connexion. Par défaut SSO.
URL de découverte discovery_url L'URL /.well-known/openid-configuration de l'IdP. Repod récupère et met en cache ce document pendant 5 minutes.
Identifiant client client_id Provenant de l'enregistrement de l'application OIDC de votre IdP.
Secret client client_secret Provenant du même enregistrement. Stocké chiffré au repos (SETTINGS_ENCRYPTION_KEY).
Scopes scopes Scopes OAuth demandés, séparés par des espaces. Par défaut openid email profile.
URI de redirection redirect_uri Laisser vide pour un calcul automatique en <app_url>/oidc-callback. À définir explicitement uniquement si app_url ne correspond pas à ce que vous avez enregistré dans l'IdP.

Cliquez sur Tester la connexion avant d'enregistrer — cela appelle POST /api/v1/auth/oidc/test-discovery avec l'URL de découverte et renvoie les authorization_endpoint, token_endpoint et jwks_uri résolus, ou une erreur claire si le document de découverte n'a pas pu être récupéré.

curl -X POST https://repod.example.com/api/v1/auth/oidc/test-discovery \
  -H "Authorization: Bearer $ADMIN_JWT" \
  -H "Content-Type: application/json" \
  -d '{"discovery_url": "https://sso.example.com/realms/myorg/.well-known/openid-configuration"}'
{
  "ok": true,
  "issuer": "https://sso.example.com/realms/myorg",
  "auth_ep": "https://sso.example.com/realms/myorg/protocol/openid-connect/auth",
  "token_ep": "https://sso.example.com/realms/myorg/protocol/openid-connect/token",
  "jwks_uri": "https://sso.example.com/realms/myorg/protocol/openid-connect/certs"
}

5. Étape 4 — Mapper les claims et le provisionnement

Champ Clé de paramètre Défaut
Provisionner automatiquement les comptes auto_provision true — crée un utilisateur Repod local à la première connexion SSO réussie
Rôle par défaut default_role reader — rôle attribué lorsqu'aucune correspondance de groupe/rôle ne s'applique
Claim username claim_username preferred_username (revient à sub si absent)
Claim email claim_email email
Claim nom complet claim_fullname name
Claim rôle claim_role vide — le claim du jeton ID portant les groupes/rôles IdP de l'utilisateur (ex. groups). Laisser vide pour toujours utiliser default_role.
Mappage des rôles role_map {} — associe une valeur de groupe/rôle IdP à un rôle Repod, ex. {"repod-admins": "admin", "repod-uploaders": "uploader"}

Le claim de rôle peut être soit une chaîne unique, soit une liste (typique pour un claim groups). Repod vérifie chaque valeur candidate par rapport à role_map et attribue la première correspondance ; si rien ne correspond, default_role s'applique.

Un utilisateur provisionné via SSO obtient auth_source="oidc" et un mot de passe local aléatoire et inutilisable — il ne peut jamais se connecter avec un mot de passe local à Repod, uniquement via SSO.

Si Provisionner automatiquement est désactivé et qu'un utilisateur inconnu termine la procédure SSO, Repod rejette la connexion avec 403 et un message lui demandant de contacter un administrateur — aucun compte n'est créé silencieusement.


6. Étape 5 — Enregistrer

Cliquez sur Enregistrer. Cela envoie une requête PATCH /api/v1/settings/ avec la section oidc :

curl -X PATCH https://repod.example.com/api/v1/settings/ \
  -H "Authorization: Bearer $ADMIN_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "oidc": {
      "enabled": true,
      "provider_name": "Corporate SSO",
      "discovery_url": "https://sso.example.com/realms/myorg/.well-known/openid-configuration",
      "client_id": "repod",
      "client_secret": "s3cr3t",
      "scopes": "openid email profile",
      "auto_provision": true,
      "default_role": "reader",
      "claim_username": "preferred_username",
      "claim_role": "groups",
      "role_map": {"repod-admins": "admin", "repod-uploaders": "uploader"}
    }
  }'

GET /api/v1/settings/ renvoie la configuration actuelle avec le client_secret masqué (••••••••) — réenregistrer sans modifier le secret est sans risque, puisque le placeholder masqué est supprimé de la mise à jour plutôt que d'écraser la valeur réellement stockée.


7. Fonctionnement du flux de connexion

  1. La page de connexion Repod appelle GET /api/v1/auth/oidc/public-config (aucune authentification requise). Si le SSO est activé et licencié, elle affiche un bouton « Se connecter avec <provider_name> ».
  2. Le frontend génère une paire code_verifier/code_challenge PKCE et un state aléatoire, puis appelle POST /api/v1/auth/oidc/authorize avec le challenge et le state. Repod renvoie l'URL d'autorisation de l'IdP.
  3. Le navigateur est redirigé vers l'IdP, l'utilisateur s'y authentifie, et l'IdP le redirige vers /oidc-callback avec un code.
  4. Le frontend appelle POST /api/v1/auth/oidc/callback avec code, state et code_verifier. Repod échange le code contre un jeton ID auprès du endpoint token de l'IdP, valide sa signature par rapport aux JWKS de l'IdP, extrait les claims, résout (ou provisionne) l'utilisateur local, et renvoie un access_token Repod normal.

À partir de ce point, le JWT émis via SSO est identique à celui émis par une connexion locale ou LDAP — chaque appel API utilise le même en-tête Authorization: Bearer <token>.


8. Vérifier que ça a fonctionné

  1. Ouvrez la page de connexion Repod dans une fenêtre de navigation privée. Confirmez que le bouton « Se connecter avec <provider_name> » apparaît.
  2. Cliquez dessus, authentifiez-vous auprès de votre IdP, et confirmez que vous atterrissez sur le tableau de bord Repod.
  3. Vérifiez Paramètres → Utilisateurs (ou GET /api/v1/auth/users, admin uniquement) pour le compte nouvellement provisionné — auth_source doit indiquer oidc.
  4. Vérifiez le journal d'audit pour une entrée OIDC_PROVISION (première connexion uniquement) suivie de OIDC_LOGIN :
    curl -H "Authorization: Bearer $ADMIN_JWT" \
      "https://repod.example.com/api/v1/audit/logs?action=OIDC_LOGIN&per_page=5"
    
  5. Si le bouton n'apparaît pas, revérifiez que enabled: true a bien été enregistré (GET /api/v1/settings/) et que votre licence inclut la fonctionnalité oidc.

9. Dépannage

Le bouton de connexion n'apparaît jamais

  • GET /api/v1/auth/oidc/public-config renvoie {"enabled": false} si oidc.enabled est faux dans les paramètres ou si la licence n'inclut pas la fonctionnalité oidc — la réponse ne distingue pas les deux cas, par conception (évite de divulguer des détails de licence sur un endpoint non authentifié).

401 sur le callback : « ID token invalide ou expiré »

  • L'horloge de l'IdP est désynchronisée par rapport au serveur Repod, ou le client_id utilisé pour valider le claim aud du jeton ne correspond pas à celui pour lequel l'IdP l'a émis. Revérifiez le champ identifiant client.

403 après une connexion IdP réussie : « Utilisateur inconnu »

  • auto_provision est désactivé et aucun compte local n'existe pour ce nom d'utilisateur. Créez le compte manuellement au préalable, ou activez le provisionnement automatique.

Le test de découverte échoue avec une erreur réseau

  • Confirmez que le conteneur backend peut atteindre l'URL de découverte : docker compose exec backend-api curl -sf <discovery_url>. Un IdP d'entreprise derrière un VPN/pare-feu peut ne pas être accessible depuis l'hôte Repod.