Contrat auth OIDC Nexus
contrat technique claims, sessions, JWKS, RBAC, step-up et tests d'acceptation
Objectif
Ce document fixe le contrat technique entre authentik et Nexus pour les lots NX-AUTH-002 a NX-AUTH-008.
Il complete l'ADR 04_Securite/23-ADR-Authentik-OIDC-MFA.md.
Variables cible
Ces variables sont le contrat cible. Elles seront ajoutees ou alignees dans .env.example par le lot qui implemente le comportement correspondant.
# OIDC / authentik
OIDC_ENABLED=false
OIDC_ISSUER_URL=https://auth.example.com/application/o/nexus/
OIDC_CLIENT_ID=nexus-ui
OIDC_CLIENT_SECRET=
OIDC_REDIRECT_URI=https://nexus.example.com/auth/callback
OIDC_POST_LOGOUT_REDIRECT_URI=https://nexus.example.com/
OIDC_SCOPES=openid email profile groups nexus.roles
OIDC_AUDIENCE=nexus-api
OIDC_EXTRA_ISSUER_URLS=
OIDC_EXTRA_AUDIENCES=
OIDC_ALLOWED_ALGORITHMS=RS256
OIDC_JWKS_CACHE_TTL_SECONDS=3600
NEXUS_M2M_CALLER_APP_CLIENTS=
# Sessions Nexus
SESSION_COOKIE_NAME=nexus_session
SESSION_COOKIE_SECURE=true
SESSION_COOKIE_SAMESITE=lax
SESSION_TTL_SECONDS=28800
STEP_UP_MAX_AGE_SECONDS=900
# Migration auth locale
LOCAL_PASSWORD_LOGIN_ENABLED=false
BREAK_GLASS_LOGIN_ENABLED=false
BREAK_GLASS_EMAILS=srey@auroramind.fr
LOCAL_USER_PASSWORD_CREATE_ENABLED=false
LOCAL_PASSWORD_ADMIN_RESET_ENABLED=false
NEXT_PUBLIC_BREAK_GLASS_EMAILS=srey@auroramind.fr
# Machine-to-machine
M2M_REQUIRED_AUDIENCE=nexus-api
M2M_DEFAULT_TOKEN_TTL_SECONDS=3600
# Webhooks
WEBHOOK_SIGNATURE_TOLERANCE_SECONDS=300
WEBHOOK_HMAC_ALGORITHM=sha256Flux humain
Login
- L'utilisateur ouvre Nexus sans session valide.
- Nexus genere
state,nonce,code_verifieretcode_challenge. - Nexus redirige vers l'authorization endpoint authentik.
- authentik authentifie l'utilisateur et applique les politiques MFA.
- authentik redirige vers
/auth/callback. - Nexus echange le code cote serveur, valide
state,nonce, token response et ID/access token. - Nexus cree une session locale
httpOnly.
Stockage tokens
Les tokens OIDC ne doivent pas etre stockes durablement dans le navigateur.
V1 recommandee :
- pas de refresh token cote navigateur;
- session Nexus bornee par
SESSION_TTL_SECONDS; - renouvellement via nouveau login authentik quand necessaire;
- si
offline_accessdevient necessaire, le refresh token doit rester cote serveur et etre chiffre ou reference par session.
Logout
Le logout doit :
- revoquer la session Nexus locale;
- supprimer le cookie;
- rediriger vers l'end session endpoint authentik si configure;
- rester robuste si authentik est temporairement indisponible.
Discovery et JWKS
Nexus charge la configuration depuis :
{OIDC_ISSUER_URL}/.well-known/openid-configurationEn mode issuer authentik par provider, l'issuer inclut le chemin /application/o/<slug>/.
Le discovery document doit fournir :
issuer;authorization_endpoint;token_endpoint;userinfo_endpointsi utilise;jwks_uri;end_session_endpointsi disponible;grant_types_supported;code_challenge_methods_supported.
Nexus doit refuser la configuration si issuer ne correspond pas exactement a OIDC_ISSUER_URL normalise.
Validation JWT
Nexus valide au minimum :
| Element | Regle |
|---|---|
| Signature | verification via JWKS |
| Algorithme | present dans OIDC_ALLOWED_ALGORITHMS; HS256 interdit pour OIDC |
| Issuer | egal a OIDC_ISSUER_URL |
| Audience | contient OIDC_AUDIENCE ou l'OIDC_CLIENT_ID Authentik |
| Expiration | exp futur |
| Not before | nbf respecte si present |
| Issued at | iat raisonnable |
| Subject | sub non vide |
| Scope | scope ou scp parse |
| Tenant | tenant_id ou mapping Nexus valide |
Claims requis
Humain
{
"iss": "https://auth.example.com/application/o/nexus/",
"aud": ["nexus-api"],
"sub": "authentik-user-id",
"email": "user@example.com",
"name": "User Name",
"groups": ["tenant-a-nexus-admin"],
"scope": "openid email profile nexus.query nexus.search",
"tenant_id": "tenant-a",
"auth_time": 1781540000,
"amr": ["pwd", "mfa", "webauthn"]
}Machine
{
"iss": "https://auth.example.com/application/o/nexus-m2m/",
"aud": ["nexus-api"],
"sub": "service-account-or-client-subject",
"client_id": "nexus-n8n-worker",
"scope": "nexus.ingest",
"tenant_id": "tenant-a"
}Machine-to-machine Authentik
Le mode M2M retenu pour Nexus est le flux OAuth2 client_credentials fourni par Authentik.
Contrat Nexus :
- le token est valide par discovery/JWKS comme les autres tokens OIDC;
auddoit contenirOIDC_AUDIENCEou l'OIDC_CLIENT_IDdu provider Authentik;- si un provider Authentik M2M dedie a une integration est utilise, son issuer
doit etre declare explicitement dans
OIDC_EXTRA_ISSUER_URLSet sonclient_id/audience dansOIDC_EXTRA_AUDIENCES; tenant_idest obligatoire;scopeouscpporte les droits Nexus minimaux;client_idouazpidentifie le client machine;grant_type=client_credentials,gty=client_credentialsou l'absence de claims humains avecclient_idclasse le principal comme machine.
Les flux machine ne passent jamais par une MFA interactive. La securite repose sur :
- client/service account dedie par integration;
- TTL court des access tokens;
- scopes minimaux (
nexus.ingestsansnexus.exportsi l'integration n'exporte pas); - binding optionnel
caller_app -> client_idcote Nexus viaNEXUS_M2M_CALLER_APP_CLIENTSquand une integration poste uncaller_app; - rotation du secret/app password token dans Authentik;
- absence de secret brut dans le depot Nexus.
Integration B DATA Studio
B DATA Studio utilise un client OAuth2 technique dedie pour exporter des rapports HTML vers Nexus. Ce flux est serveur-a-serveur : le frontend B DATA ne transmet pas de token Nexus utilisateur et le provider Authentik n'est pas rattache a un login humain.
Configuration Authentik attendue :
| Champ | Valeur |
|---|---|
| Instance | https://auth.auroramind.fr |
| Token endpoint | https://auth.auroramind.fr/application/o/token/ |
| Application / slug | bdata-nexus-m2m |
| Provider | B DATA Nexus M2M |
client_id | bdata-nexus-worker |
| Grant effectif | client_credentials |
| Issuer | https://auth.auroramind.fr/application/o/bdata-nexus-m2m/ |
| Audience | bdata-nexus-worker |
| Scopes | openid profile email nexus.ingest nexus.query nexus.search |
Variables Nexus correspondantes :
OIDC_EXTRA_ISSUER_URLS=https://auth.auroramind.fr/application/o/bdata-nexus-m2m/
OIDC_EXTRA_AUDIENCES=bdata-nexus-worker
NEXUS_M2M_CALLER_APP_CLIENTS=b_data:bdata-nexus-workerVariables serveur B DATA attendues :
BDS_NEXUS_AUTH_MODE=authentik_client_credentials
BDS_NEXUS_OIDC_CLIENT_ID=bdata-nexus-worker
BDS_NEXUS_OIDC_CLIENT_SECRET=<secret Authentik hors depot>
BDS_NEXUS_OIDC_TOKEN_URL=https://auth.auroramind.fr/application/o/token/
BDS_NEXUS_OIDC_SCOPES=openid profile email nexus.ingest nexus.query nexus.search
BDS_NEXUS_OIDC_AUDIENCE=bdata-nexus-worker
BDS_ALLOWED_NEXUS_BASES=https://nexus.auroramind.frVerification minimale sans publier de JWT brut :
- Obtenir un token
client_credentialscote serveur B DATA. - Verifier que
issvaut l'issuer M2M B DATA, queaudcontientbdata-nexus-worker, que le client estbdata-nexus-workeret qu'au moins un scopenexus.*est present. - Appeler
GET /api/auth/oidc/meavec le bearer M2M. - Attendre
is_machine=true,client_id=bdata-nexus-workeret les scopes Nexus. - Tester
GET /api/source-apps?locale=fr-FRpuis le workflow ingestionupload/init, upload presigne,upload/commitaveccaller_app=b_data, et pollingGET /api/ingest/jobs/{job_id}.
Note Authentik : la documentation officielle M2M indique que les clients machine utilisent le grant client_credentials; l'identification se fait cote Authentik par client/service account et l'authentification par token/app password selon la configuration du provider. La configuration exacte reste a valider dans l'interface admin Authentik du tenant cible.
Note audience Authentik : les JWT emis par le provider OAuth2/OIDC Authentik portent l'audience du client_id du provider. Nexus accepte donc OIDC_CLIENT_ID en plus de OIDC_AUDIENCE pour les bearers API.
Intégrations M2M V1 : scopes et binding
Les APIs /api/v1/integration-scopes, /api/v1/media,
/api/v1/domain-projections et /api/v1/knowledge-graph appliquent les
contrôles cumulés suivants :
- principal OIDC classé machine ;
caller_appconfiguré dansNEXUS_M2M_CALLER_APP_CLIENTS;client_idou sujet du bearer autorisé pour ce caller ;source_app == caller_appdans le contrat actuel ;- scope OAuth Nexus requis ;
- permission
can_readoucan_writesur la source ; - résolution du
tenant_idet du workspace côté Nexus.
| Famille | Lecture | Mutation |
|---|---|---|
| Integration scopes | nexus.query + can_read | nexus.ingest + can_write |
| Media V1 | nexus.query + scope opaque actif | nexus.ingest + scope opaque actif |
| Domain projections | nexus.query pour lecture/réconciliation | nexus.ingest pour upsert/retract/restore |
| Knowledge Graph workspace | nexus.query | nexus.ingest pour rebuild |
Le external_scope_id n'est pas une autorisation : il ne devient utilisable
qu'après résolution d'un mapping actif appartenant au même tenant, caller et
source. Une ressource hors périmètre reste non énumérable.
Le client Narrélia livré utilise un principal technique dédié, lié à
caller_app=narrelia. Les routes génériques Media V1 réutilisent actuellement
ce backend privé ; leur ouverture à un autre caller exige un lot de sécurité
explicite. Le contrat fonctionnel complet est dans
../07_Reference-Tech/56-API-Integrations-M2M-V1.md.
Exemples de profils :
| Client | Scopes Nexus | Usage |
|---|---|---|
nexus-n8n-worker | nexus.ingest | Deposer des documents et lancer l'ingestion. |
bdata-nexus-worker | openid profile email nexus.ingest nexus.query nexus.search | Export serveur B DATA Studio, destinations et ingestion HTML. |
narrelia-nexus-worker | nexus.ingest nexus.query | Scopes opaques, projections, médias privés et KG workspace Narrélia. |
nexus-intelligence-reader | nexus.query | Lire des context packs / requetes RAG. |
nexus-exporter | nexus.query nexus.export | Export controle, a eviter par defaut. |
Exemple de binding Nexus :
NEXUS_M2M_CALLER_APP_CLIENTS=b_data:bdata-nexus-workerCe binding ne remplace pas les permissions historiques : les applications non
migrees Authentik continuent d'utiliser les sessions/JWT Nexus. Une app peut
etre migree plus tard en ajoutant son client Authentik et, si elle fournit un
caller_app, son entree dans NEXUS_M2M_CALLER_APP_CLIENTS.
Rotation recommandee :
- Creer un nouveau token/app password cote Authentik.
- Mettre a jour le secret dans le coffre runtime de l'integration cliente.
- Verifier l'obtention d'un nouveau token
client_credentials. - Revoquer l'ancien token/app password.
- Verifier qu'aucune valeur brute n'a ete journalisee ni commitee.
Principal Nexus
Le backend normalise les claims vers ce modele logique :
class AuthPrincipal:
subject: str
email: str | None
name: str | None
tenant_id: str
groups: list[str]
scopes: set[str]
roles: set[str]
auth_time: int | None
amr: list[str]
is_machine: bool
client_id: str | Noneis_machine vaut true si le token vient d'un flux M2M valide ou si client_id/grant type et policy Nexus le classent explicitement comme machine.
Scopes API Nexus
Scopes cibles :
nexus.query
nexus.search
nexus.ingest
nexus.export
nexus.delete
nexus.admin
nexus.users.manage
nexus.api_keys.manage
nexus.connectors.manage
nexus.audit.readRegle : l'absence de scope requis refuse l'appel avant toute logique metier.
Roles Nexus
Roles cibles :
tenant_owner
tenant_admin
corpus_manager
analyst
reader
external_app
ingestion_workerMapping groupe recommande pour POC :
<tenant>-nexus-owner -> tenant_owner
<tenant>-nexus-admin -> tenant_admin
<tenant>-nexus-corpus-manager -> corpus_manager
<tenant>-nexus-analyst -> analyst
<tenant>-nexus-reader -> reader
<tenant>-nexus-n8n-worker -> ingestion_worker
<tenant>-nexus-external-app -> external_appPour production, Nexus doit verifier le mapping via une regle ou une table interne. Le prefixe de groupe seul ne suffit pas comme preuve tenant.
Endpoints attendus
| Endpoint | Lot | Regle |
|---|---|---|
GET /auth/login | NX-AUTH-002 | redirection authentik |
GET /auth/callback | NX-AUTH-002 | echange code server-side |
POST /auth/logout ou GET /auth/logout | NX-AUTH-002 | logout local + IdP |
GET /auth/me | NX-AUTH-002/003 | principal Nexus courant |
| dependencies FastAPI | NX-AUTH-003/004 | get_current_principal, require_scope, require_role |
| step-up dependency | NX-AUTH-008 | require_recent_mfa |
Migration depuis l'auth locale
Les lots doivent conserver un chemin de rollback tant que l'OIDC n'est pas valide en staging.
Plan cible :
NX-AUTH-002ajoute OIDC sans supprimer brutalement l'ancien code.NX-AUTH-005masque et borne le login password local.NX-AUTH-005desactive reset password en flux normal.- Les champs DB existants restent tant qu'une migration destructive n'est pas explicitement validee.
Etat NX-AUTH-005:
- le login local password est refuse si
LOCAL_PASSWORD_LOGIN_ENABLED=false, sauf siBREAK_GLASS_LOGIN_ENABLED=trueet que l'e-mail appartient aBREAK_GLASS_EMAILS; - les evenements d'authentification distinguent
oidc,password_legacyetbreak_glass; - la creation admin d'un utilisateur Nexus en mode OIDC peut generer un hash aleatoire non connu plutot que demander un mot de passe utilisateur;
- le reset password admin est bloque hors break-glass si
LOCAL_PASSWORD_ADMIN_RESET_ENABLED=false; - l'UI login affiche
Auroramind Secure Logincomme action principale quandNEXT_PUBLIC_OIDC_ENABLED=true, avec le formulaire local comme secours visible.
Tests d'acceptation
OIDC/JWT
- token valide accepte;
- token expire refuse;
- issuer incorrect refuse;
- audience incorrecte refusee;
- algorithme non autorise refuse;
- JWKS cache utilise puis rafraichi;
- scope manquant refuse.
RBAC tenant
- utilisateur sans
nexus.queryrefuse sur query; - tenant A ne lit pas tenant B;
- role admin tenant ne devient pas admin global sans mapping Nexus;
- refus audite.
M2M
- client avec
nexus.ingestautorise sur ingestion; - client sans scope requis refuse;
- logs affichent machine/client distinctement d'un utilisateur humain;
- aucune MFA interactive.
Step-up
- action sensible refusee si
auth_timetrop ancien; - admin avec e-mail OTP seul refuse si
amr/acrpermet la distinction; - action sensible auditee.
Webhooks
- signature valide acceptee;
- signature invalide refusee;
- timestamp trop ancien refuse;
key_idinconnu refuse;- secret jamais logue.
Stop conditions pour implementation
- authentik ne fournit pas une cle de signature asymetrique/JWKS;
issuerouaudiencenon stabilises;- claims
tenant_id,groups,scope/scpimpossibles a obtenir ou mapper; - besoin d'exposer un secret client au navigateur;
- absence de strategie break-glass;
- impossibilite de tester avec JWKS fake local.