20 KiB
Verification — Phase 09 (Authentik + Admin-SSO)
Verifier: general-purpose subagent (read-only review)
Datum: 2026-05-16
Files geprüft: README.md, authentik-setup.md (#81), admin-oidc-login.md (#82)
Referenz: ../DETAIL-MD-TEMPLATE.md, ../AGENT-INSTRUCTIONS.md, ../00-execution-order.md
TL;DR
Phase 09 ist inhaltlich exzellent vorbereitet — eine der vollständigsten Phasen im gesamten Improvement-Plan. Beide Detail-MDs decken Crypto-Pflichten (State/PKCE/Nonce/JWKS), JIT-Provisioning, Fallback-Strategie, Disaster-Recovery, Audit-Wiring und Defense-in-Depth-MFA sauber ab. Die Infra-Sizing-Annahmen sind realistisch, Coolify/Traefik-Integration ist konkret, und der Backlog-Hook für #66 (Tenant-SSO) ist explizit über den deaktivierten zweiten Provider verdrahtet.
Findings: 3 Major (Process/Convention), 6 Minor (Konsistenz/Klarheit), 4 Nice-to-have (Empfehlungen). Empfehlung: Mit Minor-Fixes ready für Execution. Major #1 (CLAUDE.md-Routes-Konvention) sollte vor Start adressiert werden.
1. Template-Konformität (Section-Reihenfolge)
Template-Pflichtreihenfolge laut DETAIL-MD-TEMPLATE.md:
Frontmatter → Problem → Depends on → Blocks → Approach → Implementation Steps → Test Requirements → Acceptance Criteria → Rollback Strategy → Security Notes → Subagent Recommendations → Staging-Deployment-Steps → Deviations from Plan → Progress Log → Lessons Learned
| Datei | Sections vollständig | Reihenfolge korrekt | Frontmatter komplett |
|---|---|---|---|
authentik-setup.md |
Ja (alle 15) | Ja | Ja (Status/Task-ID/Phase/Effort/Owner) |
admin-oidc-login.md |
Ja (alle 15) | Ja | Ja |
README.md ist keine Detail-MD und folgt einer eigenen Phase-Übersichts-Struktur (Phase-Done-Definition, Dependency-Graph, Phase-Summary-Slot) — passt zum Stil der anderen Phase-READMEs und ist gut.
Ergebnis: Konform.
2. Dependency-Konsistenz
2.1 Interne Reihenfolge
authentik-setup.md(#81) deklariert sich als Blocker füradmin-oidc-login.md(#82) — korrektadmin-oidc-login.md(#82) führt #81 als Dependency — korrekt00-execution-order.mdlistet beide in dieser Reihenfolge ("sequenziell") — konsistent- README.md Task-Reihenfolge-Tabelle matched
2.2 Cross-Phase-Dependencies (deklariert vs. tatsächlich)
| Deklariert in #81/#82 | Cross-Check | Status |
|---|---|---|
02-observability/sentry-selfhost-setup.md (#29) |
00-execution-order.md Phase-09-Sektion bestätigt |
OK |
02-observability/sentry-wiring-api-worker.md (#30) (in #81 als "best practice") |
Konsistent | OK |
01-tech-debt-foundation/dep-updates.md (#28) ("best practice, kein hartes Block") |
Konsistent | OK |
01-tech-debt-foundation/totp-consolidation.md (#26) in #82 |
Korrekt verlinkt | OK |
11-backlog/customer-tenant-sso.md (#66) als Blocked-By beider |
Konsistent in README + beiden Detail-MDs | OK |
2.3 Diskrepanz 00-execution-order.md
00-execution-order.md §"Phase 09 — Authentik + Admin-SSO":
Depends on: Phase 02 (Sentry für Authentik-Container)
Nennt nur Phase 02. Die Detail-MD authentik-setup.md listet zusätzlich dep-updates.md aus Phase 01 (als "best practice, kein hartes Block"), und admin-oidc-login.md listet totp-consolidation.md (#26) aus Phase 01 als hart abhängig. → Inkonsistenz Minor #M1.
00-execution-order.md sollte zusätzlich Phase 01 (mind. #26) als Hard-Dependency für Phase 09 nennen, sonst kann ein Agent #82 starten ohne dass #26 fertig ist.
3. Implementation-Steps — Qualitäts-Review
3.1 Authentik docker-compose (4 Container, 2 GB RAM Minimum)
- 4 Container (server/worker/postgres/redis) — entspricht dem offiziellen Authentik-Compose. Korrekt.
- Sizing-Annahme 2 GB RAM hartes Minimum, 4 GB empfohlen — realistisch. Authentik-Docs nennen für Production tatsächlich ~2 GB Floor; Worker + Server zusammen sind die größten Verbraucher. Healthchecks (
/-/health/live/,ak healthcheck,pg_isready,redis-cli ping) sauber spezifiziert. mem_limit: 2gpro Container ist im Override-Compose erwähnt — klar.- Lücke Minor #M2: Es fehlt eine Notiz zu Hetzner-Server-Mindestgröße. Bei Co-Location mit der App + Sentry + Authentik auf einem CX31 (8 GB) wird's eng. Im README wird "separater gehärteter Server für Production" empfohlen, aber für Staging fehlt ein expliziter Mindest-vCPU/RAM-Hinweis.
3.2 Traefik/TLS auf auth.<domain>
- Step 4 dokumentiert Traefik-Labels (Host-Rule, websecure-Entrypoint, letsencrypt-Cert-Resolver, HSTS, X-Frame-Options=DENY, Referrer-Policy)
- CSP-Header explizit über Traefik-Middleware (nicht im Container) — gute Entscheidung wegen Authentik-Eigen-CSP-Konflikt-Potenzial
- Lücke Minor #M3: Wildcard-Cert-Hinweis für
auth.*-Subdomain ist nur in "Lessons Learned" als typische Stolperfalle erwähnt. Sollte in Implementation-Step 4 explizit dokumentiert sein (DNS-Challenge vs. HTTP-Challenge bei Subdomain-Setup).
3.3 Bootstrap-Script + Blueprints
- 7 Blueprints (00-flows, 10-groups, 20-policies, 30-providers, 40-applications, 50-outposts, 60-tenants, 70-brute-force, 80-notifications)
- Idempotenz erwähnt,
bootstrap.shwartet auf/-/health/ready/ - Zwei OIDC-Provider-Definitions im 30-providers-Blueprint:
toolctrl-admin-panel(aktiv) +toolctrl-tenant-sso(Platzhalter,enabled: false) — perfekt für #66-Vorbereitung - Token-TTLs explizit: access_code=1min, access_token=10min, refresh=7d, id_token=10min — RFC-konform und konservativ
- Sinnvoll und sauber.
3.4 Authentik-Groups + Mapping zu AdminUser
toolctrl-admin(attributerole: super_admin) undtoolctrl-staff(attributerole: staff)- Mapping in #82 step 7 (
mapGroupsToRole): höchste Prioritätsuper_admin, sonststaff, sonstnull→ 403 - Re-Sync bei jedem Login (Authentik = Source-of-Truth) — korrekt, dokumentiert in Approach + Tests
- Sauber.
3.5 MFA-Pflicht für Admin-Group
20-policies.yamlenthält Expression-Policyrequire-mfa-totp-for-admin- Im Flow vor Success-Stage — Security-Notes bestätigt dass das MUSS ist
- E2E-Test "MFA-Enrollment-Flow durchspielbar" deckt das ab
- Pflicht durchgesetzt, gut.
3.6 OIDC-Client in apps/web/app/modules/admin-sso/ mit openid-client
- Library-Wahl mit klarer Begründung (vs. oidc-client-ts/next-auth/eigenbau)
- Modul-Struktur:
client.server.ts,state.server.ts,jit-provisioning.server.ts,claims.server.ts,logout.server.ts,audit.server.ts— klare Separation-of-Concerns - Discovery via
Issuer.discover(env.AUTHENTIK_ISSUER_URL), JWKS-Auto-Cache durch Library - Korrekt.
3.7 Routes /auth/admin/login + /auth/admin/callback
- PKCE (
generators.codeVerifier/codeChallenge), State, Nonce alle drei increateLoginChallenge() - Single-use Cookie
admin_sso_challengemit HttpOnly+Secure+SameSite=Lax+Path=/auth/admin+MaxAge=600s — sauber - Callback verifiziert State/Nonce/Signature/iss/aud/exp +
email_verified(in Security-Notes explizit) - Session-Fixation-Schutz (Session-ID-Rotation nach Callback) in Security-Notes — gut, sollte aber in Implementation-Step 6 als konkreter Sub-Step landen → Minor #M4
- Secure-by-Design.
3.8 JIT-Provisioning
- 3-Stufen-Lookup (ssoSubjectId → email-Fallback → 403)
- Transaction-safe + Unique-Constraint-Schutz gegen Race-Conditions
- Test-Cases dazu in Test-Requirements
- Sauber.
3.9 Fallback-Login via ADMIN_FALLBACK_LOGIN_ENABLED + Email-Allowlist
- Env-gegated, Allowlist via
ADMIN_FALLBACK_LOGIN_EMAILS assertFallbackLoginAllowed-Server-Guard VOR Passwort-Check- AuditEvent severity=warn + Sentry-Breadcrumb
- Disclosure-Toggle nur wenn Flag true, Default false
- Brute-Force-Schutz separat erwähnt (Security-Notes)
- Als Emergency-Path ausreichend gehärtet.
- Empfehlung NTH #N1: Optional Slack/Discord-Alert ist erwähnt — sollte als konkreter Sub-Step im Doku/Operator-Runbook landen statt nur als "optional hook later".
3.10 Logout-Flow mit RP-Initiated-Logout
- Step 8: Lokale Session-Kill +
client.endSessionUrl({id_token_hint, post_logout_redirect_uri}) - Fallback wenn
id_tokenfehlt (Fallback-Login-Pfad): nur lokale Session, kein Authentik-Redirect — richtig gedacht - Logout-Idempotenz in Security-Notes erwähnt
- Korrekt.
3.11 App-MFA-Layer NACH OIDC für sensible Actions
SENSITIVE_ADMIN_ACTIONS = ['impersonation.start', 'tenant.delete', 'permission.grant', 'audit.export', 'data.export']requireAppMfa(request, actionKey)mit 15-Min Server-Side-Cache (lastAppMfaAtin Session-Store, NICHT Cookie)- Nutzt konsolidierte otpauth aus #26 (explizit als Acceptance-Criteria geprüft)
- Defense-in-Depth-Begründung in Approach klar
- Tests dazu in Unit + E2E
- Funktioniert konzeptionell, gut isoliert.
4. Test-Requirements
4.1 OIDC-Flow-Tests via Mock-Authentik
mswoder Lokal-Container als Mock erwähnt- Konkrete negative-Tests: State-Mismatch, Nonce-Mismatch, Expired-Token, Empty-Group-Claim
- Vorhanden — gut.
- Empfehlung NTH #N2:
openid-clientselbst hat keine Test-Doubles als first-class-feature;msw-Setup für OIDC-Endpoints sollte als wiederverwendbares Fixture inapps/web/tests/fixtures/authentik-mock.tsangelegt werden (auch nützlich für #66 später). Sollte als konkreter Sub-Schritt im Test-Block landen.
4.2 Playwright-E2E des Login-Flows
- 6 Szenarien: Happy-Path-SSO, JIT-Provisioning, Logout, Fallback-Erfolg, Fallback-Reject, Fallback-Toggle-Hidden, App-MFA-Gate
- Gegen Staging-Authentik — explizit
- Komplett.
4.3 Fallback-Recovery-Drill
- Sowohl in
Test Requirementsals auch inStaging-Deployment-Steps(#12) als manueller Drill spezifiziert - "NICHT als completed markieren bevor … MANUELL einmal end-to-end durchgespielt"
- Pflicht-Drill korrekt verankert.
4.4 Restore-Drill für Authentik-Postgres
infra/authentik/scripts/restore-drill.shals Test-File spezifiziert- 24h-Backup-Verifikation + manueller Restore mit Smoke-Login danach
- Korrekt.
4.5 Coverage-Target
≥85%Lines/Branches im Modul,100%aufclaims.server.ts+state.server.ts(security-kritisch) — angemessen scharf.
5. Security-Notes — Vollständigkeit
| Aspekt | Abdeckung |
|---|---|
| State + Nonce + PKCE Pflicht | Ja (Security-Notes + Tests negative-Cases) |
| JWT-Signature gegen JWKS | Ja (Auto via openid-client, mit Library-Upgrade-Hinweis) |
email_verified=true prüfen |
Ja (explizit, mit Begründung Email-Hijack) |
| Audit jeder Admin-Login/Logout in AuditEvent | Ja (Step 12 + Audit-Wiring-Tabelle) |
| Session-Fixation (ID-Rotation nach Callback) | Ja (Security-Notes) — siehe Minor #M4 |
| Code-Verifier nicht loggen | Ja (Sentry-Breadcrumb-Filter erwähnt) |
| Group-Claim als Trust-Anchor | Ja (mit Hinweis auf Authentik-Hardening) |
| Fallback-Bypass-Risiko | Ja (kurze Allowlist, warn-Severity, Operator-Pflicht) |
| App-MFA-Cache nicht im Cookie | Ja (explizit Session-Store) |
| Logout-Idempotenz | Ja |
| Redirect-URI-Whitelist + returnTo-Sanitize | Ja |
| Brute-Force-Schutz auf Fallback-Login | Ja |
| Audit-Event-Inhalt: keine Secrets | Ja (id_token NIEMALS persistieren) |
AUTHENTIK_SECRET_KEY-Loss-Implications (kann nicht rotiert werden ohne Daten-Verlust) ist in #81-Rollback + Security-Notes explizit — sehr wichtig und korrekt dokumentiert.
Ergebnis: Security-Coverage exzellent.
6. Staging-Deployment-Steps
6.1 Authentik-Coolify-Deploy
- Sehr detailliert: Service-Anlage, Compose-Path, Domain, Env-Vars aus Vault, Initial-Bootstrap, Smoke-Tests, Backup-Verifikation, Restore-Drill, Brute-Force-Smoke
openssl rand-Befehle für Secret-Generierung konkret- Vollständig.
6.2 Initial-Admin-Bootstrap
AUTHENTIK_BOOTSTRAP_PASSWORD+ zwingender Wechsel beim ersten Login- Recovery-Key generiert + in Coolify-Vault (NICHT in Git)
- Sauber.
6.3 Restore-Drill
- Explizit: Dump in leere Postgres restoren, Authentik gegen Test-DB starten, Smoke-Login
restore-drill.sh-Script als Helper- Konkret.
6.4 Recovery-Drill für Fallback-Login
- In
admin-oidc-login.mdStep 12 (Staging) vollständig durchspielt inklusive Authentik-Stack-Stop, Env-Flag-Toggle, Bootstrap-Login, Authentik-Recovery, Flag-wieder-aus - Im Doku-File
docs/operations/admin-sso.mdals Fallback-Recovery-Procedure dokumentiert - Pflicht-Drill korrekt.
7. Konsistenz mit User-Wünschen
| User-Wunsch | Status |
|---|---|
| "SSO Authentik" — alles Authentik-basiert | Ja, kein Mix mit anderen IdPs |
| "Self-host" — Docker-Compose vollständig | Ja (Compose + Override + Backup + Bootstrap + Blueprints) |
| Customer-SSO-Backlog baut auf Authentik auf | Ja — toolctrl-tenant-sso-Provider als deaktivierter Platzhalter, Modul admin-sso/ als Wiederverwendungs-Foundation explizit als Acceptance #82 |
Erfüllt.
8. Fehlende Aspekte / Lücken
8.1 Authentik-Update-Strategie
- Doku-Section "Update-Workflow" ist in #81 Step 11 als Mindest-Section gelistet (Stable-Tag bumpen, Pre-Update-Backup, Release-Notes-Pflicht)
- Lücke Minor #M5: Es fehlt ein konkreter Update-Cadence-Vorschlag (z.B. "monatlich Stable-Bump prüfen, Security-Patches sofort"). Sollte im Operator-Runbook landen statt nur als Doku-Section-Eintrag.
8.2 Authentik-Sentry-Integration
AUTHENTIK_SENTRY__DSN=in.env.exampleStep 3AUTHENTIK_ERROR_REPORTING__ENABLED=false(kein SaaS-Reporting, gut)- Lücke Minor #M6: Es fehlt welcher Sentry-Projekt-Name für Authentik vorgesehen ist. Wenn alles im
boilerplate-Projekt landet, gehen Authentik-Errors in den App-Error-Stream — vermutlich nicht gewünscht. Sollte als dediziertes Sentry-Projektauthentik-infrain #29 (sentry-selfhost-setup) verzahnt sein. → Doku-Cross-Link nötig.
8.3 Audit-Webhook von Authentik zu AuditEvent
- Vorbereitet als
80-notifications.yamlBlueprint mitenabled: false, expliziter Verweis auf #67 (Audit-Log-Task) - HMAC-Signing erwähnt, Endpoint
https://api.<domain>/webhooks/authentik/audit - Gut vorbereitet, aktivierung explizit später — korrekte Scope-Trennung.
8.4 Nicht abgedeckt
- Authentik LDAP-Outpost / SCIM — explizit out of scope (Step 5 nennt "keine Outposts hochfahren"), das ist okay für Phase 09. Im README sollte aber ein "Out-of-Scope"-Vermerk stehen, damit ein Agent das nicht "freiwillig" mitbaut.
- Mobile-App-OIDC-Flow — Phase 08 Capacitor (Mobile) hat keine direkte Verzahnung. Falls Customer-Tenant-SSO später Mobile-Support braucht, müsste PKCE+Native-AppAuth-Pattern dazu kommen. Erwähnung in #66-Backlog ausreichend.
9. Findings-Liste (priorisiert)
MAJOR (vor Execution adressieren)
#J1 — CLAUDE.md-Routes-Konvention (Filenames + Route-Tree)
admin-oidc-login.md legt Routes mit Dot-Notation an: auth.admin.login.tsx, auth.admin.callback.tsx, auth.admin.logout.tsx. Im Projekt-CLAUDE.md (siehe React-Router-v7-Sektion) wird explizit die explizite routes.ts-Konfiguration über Dot-Notation-File-Convention bevorzugt. Beispiele im CLAUDE.md zeigen routes/categories-layout.tsx + route(":slug", "routes/category-details.tsx") als Pattern.
→ Klären: Soll die Phase die im Projekt etablierte File-Routing-Konvention (.-getrennt) nutzen oder via expliziter routes.ts-Einträge mit kebab-case-Filenames (auth-admin-login.tsx, auth-admin-callback.tsx, auth-admin-logout.tsx)? Empfehlung: kebab-case + explizite routes.ts, da klarer und CLAUDE.md-konform. Die Detail-MD sollte angepasst werden.
Auch betroffen: admin/security.mfa-enroll.tsx, admin/security.mfa-challenge.tsx — gleiche Frage.
#J2 — 00-execution-order.md Phase-01-Dependency fehlt
00-execution-order.md §Phase 09 nennt nur Phase 02 als Hard-Dependency. admin-oidc-login.md deklariert aber totp-consolidation.md (#26) aus Phase 01 als hart abhängig (App-MFA-Layer nutzt konsolidierte otpauth).
→ Fix: 00-execution-order.md updaten: "Depends on: Phase 01 (#26 totp-consolidation für App-MFA-Layer), Phase 02 (Sentry für Authentik-Container)".
#J3 — package.json-Touch in #82 Step 1 ist gefährlich (Workspace-Setup)
admin-oidc-login.md Step 1 nennt package.json als touched file für pnpm add openid-client jose. Im Monorepo (apps/web/-Sub-Package) sollte das apps/web/package.json sein, nicht das Root-Manifest. Sollte präzisiert werden, sonst landet die Dep im falschen Workspace.
→ Fix: "Files: apps/web/package.json (Dep: openid-client, jose)".
MINOR (Klarheit / Konsistenz)
#M1 — 00-execution-order.md muss Phase 01 als Hard-Dependency für Phase 09 nennen (s. J2). (Doppelt aufgeführt zur Sichtbarkeit.)
#M2 — Hetzner-Server-Mindest-Sizing für Co-Location (App + Sentry + Authentik) fehlt. README oder #81 Approach sollte CX31/CPX31 als Minimum-Empfehlung nennen.
#M3 — Wildcard-Cert-Hinweis (auth.*-Subdomain, DNS-Challenge) nur in Lessons-Learned. Sollte als expliziter Sub-Step in #81 Step 4 (Traefik) auftauchen.
#M4 — Session-Fixation-Schutz (App-Session-ID-Rotation nach OIDC-Callback) ist in Security-Notes erwähnt, aber NICHT als konkreter Sub-Step in #82 Step 6 (Callback-Route). Implementer wird es ggf. übersehen.
#M5 — Authentik-Update-Cadence (monatlich / Security-Patches sofort) als Operator-Runbook-Eintrag fehlt.
#M6 — Sentry-Projekt-Name für Authentik (authentik-infra vs. App-Projekt) ist nicht spezifiziert. Cross-Link mit #29 (sentry-selfhost-setup) nötig.
NICE-TO-HAVE
#N1 — Slack/Discord-Alert für Fallback-Login als konkreter Operator-Hook (statt "optional later").
#N2 — msw-Authentik-Mock-Fixture als wiederverwendbares File (apps/web/tests/fixtures/authentik-mock.ts) für #82-Tests und spätere #66-Wiederverwendung.
#N3 — Out-of-Scope-Section im README ("keine Outposts, kein LDAP-Sync, kein SAML, keine SCIM-Provisioning") damit Implementing-Agent nicht "freiwillig" Scope erweitert.
#N4 — docs/operations/admin-sso.md Mermaid-Flow-Diagramme sind als Anforderung in Step 14 + Subagent-Brief erwähnt; ein Hinweis welcher Mermaid-Renderer / welches Theme genutzt werden soll wäre hilfreich (sonst kommen 3 verschiedene Stile in den Docs zusammen).
10. Phase-Done-Definition (README) vs. Detail-MDs — Konsistenz
15 Items in README Phase-Done-Definition geprüft gegen Acceptance-Criteria in #81+#82:
| README-Item | In Detail-MD verankert |
|---|---|
| 2 Tasks completed | Trivial |
| Authentik auf Staging erreichbar | #81 Acceptance |
| 2 OIDC-Provider in Authentik | #81 Acceptance |
| Groups + MFA-Pflicht | #81 Acceptance |
| Admin-Login durchläuft | #82 Acceptance |
| JIT-Provisioning erstellt AdminUser | #82 Acceptance |
| Logout RP-Initiated | #82 Acceptance |
| Email/Passwort-Fallback Env-Gated | #82 Acceptance |
| App-MFA-Layer verdrahtet | #82 Acceptance |
| 4 AuditEvent-Typen geschrieben | #82 Step 12 + Acceptance |
| Lint/typecheck/test grün | Standard |
docs/operations/authentik-setup.md |
#81 Step 11 + Acceptance |
docs/operations/admin-sso.md |
#82 Step 14 + Acceptance |
.env.example mit allen 7 Keys |
#82 Step 13 + Acceptance |
| Code-Review durch gsd-code-reviewer | Standard |
| Backup-Restore-Drill | #81 Test-Requirements |
| Phase-Summary in README | Standard |
Alle Items in den Detail-MDs verankert. Keine Inkonsistenz.
11. Abschlussbewertung
- Template-Konformität: 10/10
- Dependency-Konsistenz: 8/10 (J2/M1 noch zu fixen)
- Implementation-Detail-Qualität: 10/10 (sehr ausführlich)
- Test-Coverage-Pläne: 9/10 (kleinere NTHs)
- Security-Notes: 10/10 (exzellent)
- Staging-Steps: 10/10
- User-Wunsch-Konsistenz: 10/10
- Vollständigkeit (Fehlende Aspekte): 8/10 (M5/M6)
Gesamtbewertung: Phase 09 ist planungstechnisch ready-to-execute nach Adressierung von J1, J2, J3 und idealerweise der Minor-Fixes (M2-M6). Alle Nice-to-haves können während Execution adressiert werden.
Blocker für Execution: Keine — alle Findings sind klärbar/fix-bar ohne Konzept-Änderung. J1 (Routes-Konvention) ist mehr eine Stil-/Klärungs-Frage; falls die Projekt-Konvention die Dot-Notation tatsächlich verwendet (was in der laufenden Codebase verifiziert werden sollte), kann der Plan unverändert bleiben.
Empfehlung: Vor Phase-09-Start einmal kurz die Detail-MDs in einem Follow-up-Commit mit den Major-Fixes patchen, dann freigeben.