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 profileactivé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 :
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¶
- Connectez-vous à Repod en tant qu'administrateur.
- Naviguez vers Paramètres → SSO.
- 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¶
- 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>». - Le frontend génère une paire
code_verifier/code_challengePKCE et unstatealéatoire, puis appellePOST /api/v1/auth/oidc/authorizeavec le challenge et le state. Repod renvoie l'URL d'autorisation de l'IdP. - Le navigateur est redirigé vers l'IdP, l'utilisateur s'y authentifie, et
l'IdP le redirige vers
/oidc-callbackavec uncode. - Le frontend appelle
POST /api/v1/auth/oidc/callbackaveccode,stateetcode_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 unaccess_tokenRepod 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é¶
- 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. - Cliquez dessus, authentifiez-vous auprès de votre IdP, et confirmez que vous atterrissez sur le tableau de bord Repod.
- Vérifiez Paramètres → Utilisateurs (ou
GET /api/v1/auth/users, admin uniquement) pour le compte nouvellement provisionné —auth_sourcedoit indiqueroidc. - Vérifiez le journal d'audit pour une entrée
OIDC_PROVISION(première connexion uniquement) suivie deOIDC_LOGIN: - Si le bouton n'apparaît pas, revérifiez que
enabled: truea 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-configrenvoie{"enabled": false}sioidc.enabledest 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_idutilisé pour valider le claimauddu 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_provisionest 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.