Authentifizierung
Status: Entwurf · Spec-Kandidat: ja
Anforderung
Authentifizierung erfolgt über OIDC/OAuth2 (NFR-006).
OIDC-Provider: Authentik (gesetzt)
Als OIDC-Provider wird Authentik verwendet (self-hosted). Alle weiteren Annahmen in dieser Spec setzen das voraus.
| Aspekt | Festlegung |
|---|---|
| Provider | Authentik (self-hosted) |
| Discovery-Endpoint | https://<authentik-host>/application/o/<app-slug>/.well-known/openid-configuration |
| JWKS-Endpoint | https://<authentik-host>/application/o/<app-slug>/jwks/ |
| Token-Signatur | RS256 (Default) |
| Pro Anwendung | Eigener „Provider”+„Application”-Eintrag in Authentik (z. B. mdm-api), eigene Client-ID/Secret |
| Token-Typ | JWT (Access-Token) — direkt im API-Layer validierbar, kein zusätzlicher UserInfo-Roundtrip nötig |
| Refresh-Tokens | Aktiviert; Frontend nutzt Auth.js / NextAuth-Pattern |
| M2M / Service-Accounts | Authentik-Service-Accounts mit Client-Credentials-Flow → gemappt auf mdm.AA_SERVICE_ACCOUNT |
Claim-Mapping
Authentik liefert per Default folgende Claims, die wir verbindlich nutzen:
| Claim | Quelle (Authentik) | Verwendung im MDM |
|---|---|---|
iss | Authentik-Issuer-URL | Validierung |
sub | Authentik-User-UUID (pk als String) | AA_APP_USER.external_id |
aud | Client-ID der MDM-Anwendung | Validierung |
email | E-Mail des Users | AA_APP_USER.email |
preferred_username / name | User-Name | AA_APP_USER.display_name |
groups | Authentik-Gruppen-Memberships (Property-Mapping „Authentik default OAuth Mapping: OpenID ‘groups’“) | Mapping auf AA_PRINCIPAL_GROUP.key |
tenant_id | Custom Property-Mapping in Authentik (Pflicht für V2+) | Tenant-Auflösung |
Pflicht-Property-Mapping in Authentik
Für Multi-Tenancy ist in Authentik ein eigenes Property-Mapping vom Typ “Scope Mapping” anzulegen, das tenant_id aus einem User-Attribut (z. B. attributes.tenant_id) in den Token einbettet. Ohne dieses Mapping fehlt der Claim und der API-Layer bricht mit 401 ab (V2+).
In V1 ist der Claim optional — der API-Layer fällt auf den Default-Tenant public zurück.
Token-Validierung im API-Layer
- JWKS via Discovery-Endpoint laden, Cache-TTL ≤ 10 min, Force-Refresh bei
kid-Miss. - Signaturprüfung gegen JWKS, Algorithmus auf RS256 fest verdrahtet (kein Algorithm-Confusion).
issmuss exakt der Authentik-Issuer-URL entsprechen,audmuss die konfigurierte Client-ID enthalten,exp/nbfwerden geprüft.sub→ Lookup inmdm.AA_APP_USERüber(issuer, external_id). Unbekannter User: JIT-Provisionierung ohne Rollenvergabe (siehe Abschnitt unten).groups-Claim wird aufAA_PRINCIPAL_GROUP-Mitgliedschaft gemappt — Auto-Stub + Login-Sync (siehe Abschnitt unten).tenant_id-Claim ist Primärquelle des Tenant-Kontexts (siehe Autorisierung – Auflösung des Tenant-Kontexts).
Just-in-Time-Provisionierung (gesetzt)
Trifft ein gültiges, signaturgeprüftes Authentik-Token mit unbekanntem (issuer, sub) ein, legt der API-Layer den Benutzer automatisch an, weist aber keinerlei Rolle zu. Bis ein Administrator dem User Rollen über AA_PRINCIPAL_ROLE zuweist, liefert jede authentifizierte Anfrage 403 (kein Match in mdm.v_effective_permission, Default-Deny).
Ablauf in einer Transaktion:
INSERT INTO mdm.AA_APP_USER (issuer, external_id, email, display_name, is_active, ...)mitis_active=true. Tenant-Bindung viatenant_id-Claim (V2+) bzw.public(V1).- Keine
AA_PRINCIPAL_ROLE-Zeile. Keine Default-Group-Mitgliedschaft (Group-Sync läuft separat über OP-27 und kann später Rechte beifügen, ist aber kein Auto-Allow). INSERT INTO mdm.AA_AUDIT_LOG (action='login', actor_principal_type='user', actor_principal_id=<neu>, payload->>'jit_provisioned'='true', ...).- Folge-Anfrage des Users → Permission-Auflösung läuft in
mdm.v_effective_permissionins Leere →403mitAA_AUDIT_LOG.action='access_denied'.
Begründung: Auto-Anlage erspart manuelles User-Management bei IdP-getriebenen Organisationen, Default-Deny ohne Rollen verhindert ungewollte Rechtevererbung allein durch IdP-Mitgliedschaft. Admin sieht den neuen User in einer Inbox-Sicht (z. B. mdm.v_app_user_unassigned) und vergibt Rollen bewusst.
Operative Konsequenzen:
- Erst-Login eines neuen Mitarbeiters erzeugt automatisch eine
AA_APP_USER-Zeile, ist aber sofort handlungsunfähig. - Disable-Pfad: Admin setzt
is_active=falseoderdeleted_at→mdm.v_effective_permissionschließt den User aus. - Die Variante “Default-Reader-Rolle automatisch zuweisen” ist explizit nicht vorgesehen (verworfen).
Group-Sync aus Authentik (gesetzt, OP-27)
Bei jedem erfolgreichen Token-Validierungs-Roundtrip führt der API-Layer eine idempotente Synchronisation der Authentik-groups-Claim-Werte gegen AA_PRINCIPAL_GROUP und AA_PRINCIPAL_GROUP_MEMBER aus — in einer Transaktion am Login-Pfad, nicht per separatem Cron.
Mapping-Regel: Authentik-Group-Name → AA_PRINCIPAL_GROUP.key 1:1 nach Normalisierung (lower(), Whitespace → _, Sonderzeichen-Strip; Originalname landet in AA_PRINCIPAL_GROUP.name).
Ablauf je Login:
- Für jeden Wert im
groups-Claim:- Lookup
AA_PRINCIPAL_GROUPper normalisiertemkey. - Auto-Stub: existiert die Gruppe nicht, lege sie an mit
is_active=false,metadata={"source":"authentik","raw":"<original>"}. Stub trägt keine Rollen — bis ein Admin sie aufis_active=truesetzt und Rollen überAA_PRINCIPAL_ROLEbeibringt, hat die Gruppe keinerlei Wirkung inmdm.v_effective_permission. - Membership
AA_PRINCIPAL_GROUP_MEMBER:INSERT … ON CONFLICT DO NOTHINGfür(principal_group_id, member_principal_type='user', member_principal_id=<AA_APP_USER.id>).
- Lookup
- Group-Memberships, die nicht mehr im aktuellen Claim stehen, werden soft-gelöscht (
AA_PRINCIPAL_GROUP_MEMBER.deleted_at = now()). Authentik bleibt single source of truth für Mitgliedschaften. - Inaktive Stub-Gruppen (
is_active=false) tauchen in einer Admin-Sichtmdm.v_principal_group_unassignedauf — analog zuv_app_user_unassignedaus der JIT-Logik. - Audit-Log:
action='role_assign'/'role_unassign'werden nicht für Group-Sync geschrieben (nur für RBAC). Stattdessenaction='login'mitpayload.group_sync={ added: [...], removed: [...] }für Nachvollziehbarkeit.
Konsistenzversprechen: Authentik-Verlust einer Gruppe → User verliert vererbte Rollen sofort beim nächsten Login, ohne manuelle Pflege. Auto-Stub vermeidet, dass IdP-Group-Renames im MDM zu unsichtbaren Permission-Lücken führen.
Operative Konsequenzen:
- Eine neue Authentik-Gruppe ist im MDM beim ersten Login eines Mitglieds als inaktiver Stub sichtbar; Admin aktiviert + bindet Rollen.
- Login-Latenz steigt um die Sync-Transaktion (Anzahl Group-Claims × kleine Writes); bei sehr großen
groups-Claims kann der Sync optional in einen Hintergrund-Job verschoben werden — V1 macht es synchron. - Variante “Whitelist” (nur vorab konfigurierte Gruppen erlaubt) wird nicht umgesetzt; Whitelist-Effekt wird über
is_active=false-Stubs erreicht.
Pflichten
- Jede API-Anfrage muss authentifiziert sein (Service-zu-Service-Pfade dürfen Client-Credentials nutzen).
- Identitäten werden in
created_by,updated_by,archived_by,deleted_by,changed_by(Audit) durchgereicht. Das genaue Feldformat ist offen (z. B.sub-Claim, E-Mail, interner Benutzer-ID). - Token-Validierung gegen den OIDC-Issuer (Signaturprüfung,
aud,iss, Ablauf).
Erforderliche Token-Claims
| Claim | Pflicht | Zweck |
|---|---|---|
iss | ja | OIDC-Issuer |
sub | ja | Externer User-Identifier, gemappt auf AA_APP_USER.external_id |
aud | ja | Audience-Validierung |
exp / iat | ja | Ablauf, Issue-Time |
tenant_id | ja (V2+) | Mandantenbindung als UUID oder AA_TENANT.key. Primärquelle für Tenant-Kontext. |
| Gruppen-Claim (Name konfigurierbar) | nein | Gemappt auf AA_PRINCIPAL_GROUP.key |
Multi-Tenant-Nutzer können den aufgelösten Tenant pro Request via X-Tenant-Id-Header überschreiben. Der Service prüft dabei, ob der Principal eine aktive Rolle in diesem Tenant hat — andernfalls 403. Detail: Autorisierung – Auflösung des Tenant-Kontexts.
Offen
Wahl des OIDC-Providers.Gelöst: Authentik.JIT-Provisionierung unbekannter OIDC-Subjekte.Gelöst: Auto-AnlageAA_APP_USERohne Rollen, Admin-Freischaltung pflicht (siehe Abschnitt oben, OP-23).Konkretes MappingGelöst: Auto-Stubgroups-Claim →AA_PRINCIPAL_GROUP.key(Sync-Strategie, Auto-Create vs. Whitelist).is_active=false+ Login-Sync, Admin schaltet Gruppe + Rollenbindung frei (siehe Abschnitt oben, OP-27).- Detail-Workflow Service-Account-Erstellung in Authentik ↔
mdm.AA_SERVICE_ACCOUNT(manuell vs. provisioniert). - Session-/Token-Lebensdauer (Access-Token, Refresh-Token) — Authentik-Default vs. MDM-Vorgabe.