Implementierung war bereits in main vorhanden (Commit 9a41f3d). Dieser
Commit dokumentiert die reale Verifikation aller drei Akzeptanzkriterien
(Issuer/Audience-Bindung, JWKS-only-Signaturpruefung, Sperre gegen
automatische Superadmin-Vergabe aus externen Claims) durch Testlauf auf
Host 204.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
5.6 KiB
Prüfprotokoll IAM-18 — Strikte Claim-Validierung bei OIDC-SSO
Datum: 2026-10-15
Geprüft auf: Host 204 (/root/iam-18-work-1792096064/repo), Branch feature/iam-18-strikte-claim-validierung-bei-oidc-sso
Befund: Ticket bereits implementiert
Bei der Analyse zeigte sich, dass IAM-18 bereits vollständig im main-Branch
enthalten ist (Commit 9a41f3d, "IAM-18: strikte-claim-validierung-bei-oidc-sso",
nach QA-02 gemergt). Es war keine neue Implementierung nötig; dieses Protokoll
dokumentiert die reale Verifikation der drei Akzeptanzkriterien auf Host 204.
Relevante Dateien:
internal/oidc/verify.go—Verifier/NewVerifier/Verify/VerifyWithNonceinternal/oidc/login.go—CompleteOIDCLogin,capToTenantScopedRoleinternal/oidc/jwks.go—ParseJWKS(JWKS-Parsing des konfigurierten Issuers)internal/oidc/verify_test.go,internal/oidc/login_test.go— zugehörige Tests
Akzeptanzkriterium 1 — Issuer/Audience exakt gegen Tenant-Konfiguration
oidc.NewVerifier(keys, issuer, audience) bindet Issuer und Audience fest an
Werte, die der Aufrufer aus der je-Tenant hinterlegten Konfiguration übergibt
— niemals aus dem Token selbst gelesen. Verify() vergleicht claims.Issuer
und claims.Audience exakt (keine Präfix-/Teilstring-Logik) gegen diese
Werte und lehnt bei Abweichung mit ErrInvalidToken ab.
Negativtest ausgeführt:
TestVerify_RejectsWrongIssuer— PASSTestVerify_RejectsWrongAudience— PASS
Akzeptanzkriterium 2 — Signatur ausschließlich gegen JWKS des konfigurierten Issuers
Die Schlüsselmenge (map[string]*rsa.PublicKey) wird beim Aufrufer
ausschließlich aus ParseJWKS() der JWKS-URL des konfigurierten Issuers
befüllt und dem Verifier übergeben. Der kid-Header des Tokens wählt nur
innerhalb dieser vorab geladenen Menge aus; ein Token kann weder einen
eigenen Signaturschlüssel noch einen eigenen Issuer erzwingen.
Negativtest ausgeführt:
TestVerify_RejectsWrongSigningKey(Token mit fremdem, nicht in der konfigurierten JWKS-Menge enthaltenem Schlüssel signiert) — PASSTestVerify_RejectsExpiredToken— PASS (Ablaufprüfung als Teil derselben Verify-Pfad-Härtung)
Akzeptanzkriterium 3 — Keine automatische Superadmin-Vergabe aus externen Claims
Zweistufige Sperre:
ldapsync.RoleMappingStore(gemeinsame Mapping-Tabelle mit IAM-05/LDAP) lässt überSetMappingnuruser/tenant_adminals Zielrolle zu (CHECK-Constraintldap_group_role_mapping_role_check).capToTenantScopedRole()im OIDC-Login-Pfad selbst kappt zusätzlich und unabhängig von der Mapping-Tabelle jede gelieferterbac.RoleSuperadminaufrbac.RoleTenantAdmin— selbst wenn die Mapping-Tabelle unter Umgehung der Anwendungsschicht (z. B. fehlerhafter DB-Zugriff) aufsuperadmingesetzt würde.
Negativtests ausgeführt:
TestCompleteOIDCLogin_UnmappedRoleGrantsNothing— Claim behauptet["tenant_admin", "superadmin"]über nie gemappte Gruppen → keine Rollenzuweisung. PASSTestCompleteOIDCLogin_CapsSuperadminClaimToTenantAdmin— CHECK-Constraint der Mapping-Tabelle wird testweise entfernt und die Tabelle direkt aufsuperadmingesetzt (Simulation einer kompromittierten Zuordnung);CompleteOIDCLoginvergibt trotzdem nurtenant_admin, niemalssuperadmin. PASSTestCapToTenantScopedRole_NeverGrantsSuperadmin(reine Unit-Ebene ohne DB) — PASS
Testlauf (real ausgeführt auf Host 204)
Befehl:
set -a && source /etc/nexarch-dev/core-dev.env && set +a
go test ./internal/oidc/... ./internal/rbac/... ./internal/ldapsync/... -v -count=1
Ergebnis: alle Tests PASS.
internal/oidc: 17 Tests PASS (TestCompleteOIDCLogin_EndToEnd,TestCompleteOIDCLogin_UnmappedRoleGrantsNothing,TestCapToTenantScopedRole_NeverGrantsSuperadmin,TestCompleteOIDCLogin_CapsSuperadminClaimToTenantAdmin,TestFullAuthorizationCodeFlow,TestGrantedScopes_NeverExceedsAllowed,TestValidateRedirectURI_RejectsUnregistered,TestAuthCodeStore_CannotBeConsumedTwice,TestStateStore_ConsumeIsSingleUse,TestStateStore_RejectsUnknownState,TestVerify_AcceptsValidToken,TestVerify_RejectsExpiredToken,TestVerify_RejectsWrongSigningKey,TestVerify_RejectsWrongIssuer,TestVerify_RejectsWrongAudience,TestVerifyWithNonce_RejectsMismatch,TestParseJWKS_RoundTrip) — Laufzeit 2,1sinternal/rbac: 19 Tests PASS (u. a.TestStore_RejectsSuperadminOutsideAllowedMatrix,TestValidateNoTenantToSuperadminEscalation_*,TestAssignRole_RejectsSelfEscalation) — Laufzeit 34,7sinternal/ldapsync: 9 Tests PASS — Laufzeit 1,4s
Keine Fehlschläge, keine Skips (TEST_ADMIN_DSN war gesetzt).
Bewertung
Alle drei Akzeptanzkriterien sind durch Code und durch reale, auf Host 204 ausgeführte automatisierte Tests abgedeckt. Es waren keine Codeänderungen nötig; dieser Durchlauf dient der Abnahme-Verifikation und Dokumentation.
Offene Anmerkung (keine Ticketerweiterung, nur Beobachtung)
In cmd/ wurde kein HTTP-Server gefunden, der oidc.NewVerifier mit einer
aus einer Tenant-Konfigurationstabelle geladenen Issuer/Audience/JWKS-URL
instanziiert (nur in Tests verwendet). Die Bindung "Issuer/Audience kommen
ausschließlich aus der Tenant-Konfiguration" ist auf Code-Ebene (API von
NewVerifier) bereits erzwungen; die konkrete Tenant-Konfigurationsablage
und das Verdrahten in einen produktiven Login-Endpunkt scheinen außerhalb
des Scopes von IAM-06/IAM-18 zu liegen (kein echter externer OIDC-Provider
in dieser Umgebung verfügbar, siehe Kommentar in internal/oidc/jwks.go).
Kein Handlungsbedarf für dieses Ticket, ggf. als Folgeticket für die
produktive Tenant-OIDC-Konfigurationsverwaltung vormerken.