IAM-18: strikte-claim-validierung-bei-oidc-sso
Supply-Chain-Scan (Go) / govulncheck (push) Failing after 23s

Ergaenzt IAM-06 (SSO ueber OIDC) um strikte Issuer-/Audience-Pruefung,
JWKS-Pinning gegen den konfigurierten Issuer und eine technisch
erzwungene Sperre gegen Superadmin-Vergabe aus externen Claims.

- internal/oidc/verify.go: Verifier.audience wird per Tenant fest
  konfiguriert (NewVerifier), niemals aus dem Token gelesen; Verify
  lehnt Tokens mit unerwarteter Audience zusaetzlich zum bestehenden
  Issuer-Abgleich ab. Signatur wird weiterhin ausschliesslich gegen die
  beim Verifier hinterlegte JWKS-Schluesselmenge des konfigurierten
  Issuers geprueft (kid waehlt nur INNERHALB dieser Menge aus).
- internal/oidc/login.go: capToTenantScopedRole() kappt eine aus
  RoleMappingStore.HighestRoleFor gelieferte superadmin-Rolle
  unabhaengig von deren Quelle auf tenant_admin, statt sie zu vergeben
  oder den Login mit einem Fehler abzubrechen. Zusaetzliche, von der
  bestehenden Erlaubnis-Matrix (SetMapping, rbac.Store.Assign)
  unabhaengige Verteidigungsebene direkt im Login-Pfad.
- Tests: TestVerify_RejectsWrongAudience (Pruefung 1),
  TestVerify_RejectsWrongSigningKey (Pruefung 2, bereits vorhanden),
  TestCapToTenantScopedRole_NeverGrantsSuperadmin und
  TestCompleteOIDCLogin_CapsSuperadminClaimToTenantAdmin (Pruefung 3,
  inkl. Integrationstest mit direkt an der Erlaubnis-Matrix vorbei
  manipulierter Zuordnungstabelle).
- internal/pentest, internal/saml: bestehende Tests an neue
  NewVerifier-Signatur (audience-Parameter) angepasst.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPsyieCYwvbUBsnN598SbP
This commit is contained in:
patrickandClaude Sonnet 5 committed 2026-09-17 10:51:42 +02:00
1 parent cc0dc21fe7
commit 9a41f3def9
6 files changed
+210 -28

No files matched your search

+21 -1
View File
@@ -49,7 +49,7 @@ func CompleteOIDCLogin(
if role, ok, err := roleMapping.HighestRoleFor(ctx, claims.Roles); err != nil {
return "", err
} else if ok {
if _, err := roles.Assign(ctx, u.ID, role, "oidc-sso"); err != nil {
if _, err := roles.Assign(ctx, u.ID, capToTenantScopedRole(role), "oidc-sso"); err != nil {
return "", err
}
}
@@ -57,6 +57,26 @@ func CompleteOIDCLogin(
return issuer.Issue(u.ID, tenantSlug)
}
// capToTenantScopedRole stellt technisch und unabhaengig von der
// Erlaubnis-Matrix in ldapsync.RoleMappingStore sicher, dass aus externen
// OIDC-Claims niemals eine Superadmin-Rolle vergeben werden kann
// (Akzeptanzkriterium 3). RoleMappingStore.SetMapping laesst zwar bereits
// nur user/tenant_admin als Ziel einer Zuordnung zu, und rbac.Store.Assign
// weist superadmin-Zuweisungen ebenfalls zurueck (ErrRoleNotAssignableInTenantScope)
// — diese Funktion ist die zusaetzliche, im OIDC-Login-Pfad selbst liegende
// Sperre: selbst wenn HighestRoleFor durch eine fehlerhafte oder direkt an
// der Datenbank vorbei veraenderte Zuordnungstabelle superadmin liefern
// wuerde, kappt der Login-Pfad auf die hoechste zulaessige tenant-gescopte
// Rolle, statt entweder superadmin zu vergeben oder den gesamten Login mit
// einem Fehler abzubrechen. Superadmin bleibt ausschliesslich lokal ueber
// internal/user.SuperadminStore (IAM-01) vergebbar.
func capToTenantScopedRole(role rbac.Role) rbac.Role {
if role == rbac.RoleSuperadmin {
return rbac.RoleTenantAdmin
}
return role
}
func findOrCreateUser(ctx context.Context, users *user.TenantUserStore, email string) (user.User, error) {
creds, err := users.GetByEmailForAuth(ctx, email)
if err == nil {
+96 -4
View File
@@ -67,7 +67,7 @@ func TestCompleteOIDCLogin_EndToEnd(t *testing.T) {
ctx := context.Background()
key := generateTestKey(t)
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &key.PublicKey}, "https://idp.example.com")
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &key.PublicKey}, testIssuer, testAudience)
states := NewStateStore(pool)
users := user.NewTenantUserStore(pool)
roles := rbac.NewStore(pool)
@@ -90,7 +90,8 @@ func TestCompleteOIDCLogin_EndToEnd(t *testing.T) {
Nonce: nonce,
Roles: []string{"sso-admins"},
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "https://idp.example.com",
Issuer: testIssuer,
Audience: jwt.ClaimStrings{testAudience},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
},
}
@@ -126,7 +127,7 @@ func TestCompleteOIDCLogin_UnmappedRoleGrantsNothing(t *testing.T) {
ctx := context.Background()
key := generateTestKey(t)
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &key.PublicKey}, "https://idp.example.com")
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &key.PublicKey}, testIssuer, testAudience)
states := NewStateStore(pool)
users := user.NewTenantUserStore(pool)
roles := rbac.NewStore(pool)
@@ -146,7 +147,8 @@ func TestCompleteOIDCLogin_UnmappedRoleGrantsNothing(t *testing.T) {
// hochzustufen — diese Gruppen wurden NIE gemappt.
Roles: []string{"tenant_admin", "superadmin"},
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "https://idp.example.com",
Issuer: testIssuer,
Audience: jwt.ClaimStrings{testAudience},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
},
}
@@ -162,6 +164,96 @@ func TestCompleteOIDCLogin_UnmappedRoleGrantsNothing(t *testing.T) {
}
}
// Akzeptanzkriterium 3 + Pruefung 3: eine Unit-Ebene, die unabhaengig von
// jeder Datenbank belegt, dass der OIDC-Login-Pfad eine Superadmin-Rolle
// technisch niemals weiterreicht.
func TestCapToTenantScopedRole_NeverGrantsSuperadmin(t *testing.T) {
if got := capToTenantScopedRole(rbac.RoleSuperadmin); got != rbac.RoleTenantAdmin {
t.Fatalf("erwartet hoechste zulaessige tenant-gescopte rolle (tenant_admin) statt superadmin, habe %q", got)
}
if got := capToTenantScopedRole(rbac.RoleTenantAdmin); got != rbac.RoleTenantAdmin {
t.Fatalf("tenant_admin muss unveraendert bleiben, habe %q", got)
}
if got := capToTenantScopedRole(rbac.RoleUser); got != rbac.RoleUser {
t.Fatalf("user muss unveraendert bleiben, habe %q", got)
}
}
// Akzeptanzkriterium 3 + Pruefung 3, Integrationsebene: selbst wenn die
// Zuordnungstabelle unter Umgehung von RoleMappingStore.SetMapping (das
// bereits nur user/tenant_admin zulaesst) DIREKT auf 'superadmin' gesetzt
// wuerde — etwa durch einen fehlerhaften Migrations- oder Admin-Eingriff —
// vergibt CompleteOIDCLogin trotzdem hoechstens tenant_admin, nie superadmin,
// und bricht den Login dafuer nicht mit einem Fehler ab.
func TestCompleteOIDCLogin_CapsSuperadminClaimToTenantAdmin(t *testing.T) {
pool, cleanup := setupLoginTest(t)
defer cleanup()
ctx := context.Background()
// CHECK-Constraint der Zuordnungstabelle bewusst umgehen, um eine
// kompromittierte/fehlerhafte Zuordnung zu simulieren, die
// RoleMappingStore.SetMapping normalerweise verhindert.
if _, err := pool.Exec(ctx, `ALTER TABLE ldap_group_role_mapping DROP CONSTRAINT IF EXISTS ldap_group_role_mapping_role_check`); err != nil {
t.Fatalf("check-constraint entfernen: %v", err)
}
if _, err := pool.Exec(ctx, `
INSERT INTO ldap_group_role_mapping (ldap_group, role) VALUES ('kompromittierte-gruppe', 'superadmin')
ON CONFLICT (ldap_group) DO UPDATE SET role = 'superadmin'
`); err != nil {
t.Fatalf("manipulierte zuordnung einfuegen: %v", err)
}
key := generateTestKey(t)
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &key.PublicKey}, testIssuer, testAudience)
states := NewStateStore(pool)
users := user.NewTenantUserStore(pool)
roles := rbac.NewStore(pool)
roleMapping := ldapsync.NewRoleMappingStore(pool)
issuer := auth.NewTokenIssuer("test-secret-nur-fuer-tests")
// Gegenprobe: die (kompromittierte) Zuordnung liefert tatsaechlich
// superadmin, bevor CompleteOIDCLogin die Sperre anwendet.
if role, ok, err := roleMapping.HighestRoleFor(ctx, []string{"kompromittierte-gruppe"}); err != nil || !ok || role != rbac.RoleSuperadmin {
t.Fatalf("erwartet manipulierte zuordnung liefert superadmin, habe role=%q ok=%v err=%v", role, ok, err)
}
state, nonce, err := states.Generate(ctx)
if err != nil {
t.Fatalf("generate state: %v", err)
}
email := fmt.Sprintf("oidc-test-%d@example.com", time.Now().UnixNano())
claims := Claims{
Email: email,
Nonce: nonce,
// Der externe Claim behauptet ueber die kompromittierte Zuordnung
// effektiv eine Superadmin-Rolle.
Roles: []string{"kompromittierte-gruppe"},
RegisteredClaims: jwt.RegisteredClaims{
Issuer: testIssuer,
Audience: jwt.ClaimStrings{testAudience},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
},
}
idToken := signTestToken(t, key, "kid-1", claims)
if _, err := CompleteOIDCLogin(ctx, verifier, states, users, roles, roleMapping, issuer, "acme", state, idToken); err != nil {
t.Fatalf("complete oidc login: %v", err)
}
userID := mustGetUserID(t, ctx, users, email)
assignment, err := roles.Get(ctx, userID)
if err != nil {
t.Fatalf("rollenzuweisung lesen: %v", err)
}
if assignment.Role == rbac.RoleSuperadmin {
t.Fatal("SICHERHEITSVERLETZUNG: superadmin ueber externen oidc-claim vergeben")
}
if assignment.Role != rbac.RoleTenantAdmin {
t.Fatalf("erwartet hoechste zulaessige tenant-gescopte rolle (tenant_admin), habe %q", assignment.Role)
}
}
func mustGetUserID(t *testing.T, ctx context.Context, users *user.TenantUserStore, email string) string {
t.Helper()
creds, err := users.GetByEmailForAuth(ctx, email)
+41 -6
View File
@@ -22,23 +22,43 @@ type Claims struct {
// Verifier prueft ID-Tokens gegen den JWKS-Schluesselsatz eines konfigurierten
// Providers (Akzeptanzkriterium 3: fehlerhafte/abgelaufene Tokens werden
// abgewiesen).
//
// IAM-18: issuer und audience werden AUSSCHLIESSLICH ueber NewVerifier aus der
// je-Tenant hinterlegten Konfiguration gesetzt (Server-/Tenant-Konfiguration,
// niemals aus dem Token selbst gelesen) und bei jeder Verifikation exakt
// gegen die Token-Claims abgeglichen (Akzeptanzkriterium 1). keys wird
// ebenfalls ausschliesslich aus der JWKS-URL DIESES konfigurierten Issuers
// befuellt (siehe jwks.go/ParseJWKS) — ein Token kann weder einen eigenen
// Issuer noch einen eigenen Signaturschluessel vorschreiben
// (Akzeptanzkriterium 2).
type Verifier struct {
keys map[string]*rsa.PublicKey
issuer string
keys map[string]*rsa.PublicKey
issuer string
audience string
}
func NewVerifier(keys map[string]*rsa.PublicKey, issuer string) *Verifier {
return &Verifier{keys: keys, issuer: issuer}
// NewVerifier bindet einen Verifier fest an den fuer einen Tenant
// konfigurierten Issuer, die konfigurierte Audience und die aus dessen JWKS
// geladenen Schluessel. Alle drei Werte kommen aus der Tenant-Konfiguration
// des Aufrufers — nicht aus dem zu pruefenden Token.
func NewVerifier(keys map[string]*rsa.PublicKey, issuer, audience string) *Verifier {
return &Verifier{keys: keys, issuer: issuer, audience: audience}
}
// Verify prueft Signatur (gegen JWKS), Ablauf (jwt-Bibliothek lehnt
// abgelaufene Tokens automatisch ab) und Aussteller.
// Verify prueft Signatur (ausschliesslich gegen die uebergebenen JWKS-
// Schluessel des konfigurierten Issuers), Ablauf (jwt-Bibliothek lehnt
// abgelaufene Tokens automatisch ab), Aussteller und Audience
// (Akzeptanzkriterium 1 + 2).
func (v *Verifier) Verify(idToken string) (*Claims, error) {
claims := &Claims{}
token, err := jwt.ParseWithClaims(idToken, claims, func(t *jwt.Token) (interface{}, error) {
if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
return nil, ErrInvalidToken
}
// Der Signaturschluessel kommt AUSSCHLIESSLICH aus der beim
// Verifier hinterlegten JWKS-Schluesselmenge des konfigurierten
// Issuers — der kid-Header waehlt nur INNERHALB dieser Menge aus,
// ein Token kann keinen fremden Schluessel oder Issuer erzwingen.
kid, _ := t.Header["kid"].(string)
key, ok := v.keys[kid]
if !ok {
@@ -52,9 +72,24 @@ func (v *Verifier) Verify(idToken string) (*Claims, error) {
if claims.Issuer != v.issuer {
return nil, fmt.Errorf("%w: unerwarteter aussteller %q", ErrInvalidToken, claims.Issuer)
}
if !containsAudience(claims.RegisteredClaims.Audience, v.audience) {
return nil, fmt.Errorf("%w: unerwartete audience %q", ErrInvalidToken, claims.RegisteredClaims.Audience)
}
return claims, nil
}
// containsAudience prueft exakten Abgleich (kein Praefix-/Teilstring-Match)
// gegen den aud-Claim, der laut RFC 7519 ein einzelner String oder eine
// Liste sein kann.
func containsAudience(audience jwt.ClaimStrings, expected string) bool {
for _, a := range audience {
if a == expected {
return true
}
}
return false
}
// VerifyWithNonce prueft zusaetzlich, dass der nonce-Claim im Token exakt
// dem bei der Anfrage ausgestellten Nonce entspricht — verhindert Replay
// eines fremden, gueltigen ID-Tokens (Akzeptanzkriterium/Pruefung 3).
+43 -11
View File
@@ -12,6 +12,11 @@ import (
"github.com/golang-jwt/jwt/v5"
)
const (
testIssuer = "https://idp.example.com"
testAudience = "https://nexarch.example.com/tenants/acme"
)
func generateTestKey(t *testing.T) *rsa.PrivateKey {
t.Helper()
key, err := rsa.GenerateKey(rand.Reader, 2048)
@@ -34,14 +39,15 @@ func signTestToken(t *testing.T, key *rsa.PrivateKey, kid string, claims Claims)
func TestVerify_AcceptsValidToken(t *testing.T) {
key := generateTestKey(t)
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &key.PublicKey}, "https://idp.example.com")
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &key.PublicKey}, testIssuer, testAudience)
claims := Claims{
Subject: "user-123",
Email: "person@example.com",
Nonce: "erwarteter-nonce",
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "https://idp.example.com",
Issuer: testIssuer,
Audience: jwt.ClaimStrings{testAudience},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
},
}
@@ -59,12 +65,13 @@ func TestVerify_AcceptsValidToken(t *testing.T) {
// Akzeptanzkriterium 3 + Pruefung 2: Token-Signatur- und Ablaufpruefung.
func TestVerify_RejectsExpiredToken(t *testing.T) {
key := generateTestKey(t)
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &key.PublicKey}, "https://idp.example.com")
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &key.PublicKey}, testIssuer, testAudience)
claims := Claims{
Subject: "user-123",
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "https://idp.example.com",
Issuer: testIssuer,
Audience: jwt.ClaimStrings{testAudience},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(-time.Hour)), // abgelaufen
},
}
@@ -79,12 +86,13 @@ func TestVerify_RejectsWrongSigningKey(t *testing.T) {
realKey := generateTestKey(t)
attackerKey := generateTestKey(t)
// Verifier kennt NUR den echten oeffentlichen Schluessel.
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &realKey.PublicKey}, "https://idp.example.com")
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &realKey.PublicKey}, testIssuer, testAudience)
claims := Claims{
Subject: "user-123",
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "https://idp.example.com",
Issuer: testIssuer,
Audience: jwt.ClaimStrings{testAudience},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
},
}
@@ -98,7 +106,7 @@ func TestVerify_RejectsWrongSigningKey(t *testing.T) {
func TestVerify_RejectsWrongIssuer(t *testing.T) {
key := generateTestKey(t)
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &key.PublicKey}, "https://idp.example.com")
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &key.PublicKey}, testIssuer, testAudience)
claims := Claims{
Subject: "user-123",
@@ -114,16 +122,40 @@ func TestVerify_RejectsWrongIssuer(t *testing.T) {
}
}
// Akzeptanzkriterium 1 + Pruefung 1: falscher Issuer/Audience wird abgelehnt.
// Issuer und Audience kommen ausschliesslich aus der beim Verifier fest
// hinterlegten Tenant-Konfiguration (siehe NewVerifier-Aufruf oben) — das
// Token selbst kann seine eigene Audience nicht durchsetzen.
func TestVerify_RejectsWrongAudience(t *testing.T) {
key := generateTestKey(t)
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &key.PublicKey}, testIssuer, testAudience)
claims := Claims{
Subject: "user-123",
RegisteredClaims: jwt.RegisteredClaims{
Issuer: testIssuer,
Audience: jwt.ClaimStrings{"https://ein-anderer-mandant.example.com"},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
},
}
tok := signTestToken(t, key, "kid-1", claims)
if _, err := verifier.Verify(tok); err == nil {
t.Fatal("erwartet fehler bei unerwarteter audience")
}
}
// Akzeptanzkriterium/Pruefung 3: Nonce-Handling gegen Replay.
func TestVerifyWithNonce_RejectsMismatch(t *testing.T) {
key := generateTestKey(t)
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &key.PublicKey}, "https://idp.example.com")
verifier := NewVerifier(map[string]*rsa.PublicKey{"kid-1": &key.PublicKey}, testIssuer, testAudience)
claims := Claims{
Subject: "user-123",
Nonce: "nonce-a",
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "https://idp.example.com",
Issuer: testIssuer,
Audience: jwt.ClaimStrings{testAudience},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
},
}
@@ -173,9 +205,9 @@ func TestParseJWKS_RoundTrip(t *testing.T) {
}
// Der wiederhergestellte Schluessel muss ein damit signiertes Token auch tatsaechlich verifizieren.
verifier := NewVerifier(parsed, "https://idp.example.com")
verifier := NewVerifier(parsed, testIssuer, testAudience)
claims := Claims{Subject: "x", RegisteredClaims: jwt.RegisteredClaims{
Issuer: "https://idp.example.com", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
Issuer: testIssuer, Audience: jwt.ClaimStrings{testAudience}, ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
}}
tok := signTestToken(t, key, "kid-1", claims)
if _, err := verifier.Verify(tok); err != nil {
+7 -4
View File
@@ -117,7 +117,7 @@ func TestPentest_OIDC_AlgNoneAttack(t *testing.T) {
if err != nil {
t.Fatalf("rsa key: %v", err)
}
verifier := oidc.NewVerifier(map[string]*rsa.PublicKey{"kid-1": &rsaKey.PublicKey}, "https://idp.example.test")
verifier := oidc.NewVerifier(map[string]*rsa.PublicKey{"kid-1": &rsaKey.PublicKey}, "https://idp.example.test", "https://nexarch.example.test/tenants/pentest")
claims := jwt.MapClaims{
"sub": "angreifer@example.test",
@@ -150,7 +150,7 @@ func TestPentest_OIDC_ForeignKeySignature(t *testing.T) {
if err != nil {
t.Fatalf("attacker key: %v", err)
}
verifier := oidc.NewVerifier(map[string]*rsa.PublicKey{"trusted-kid": &trustedKey.PublicKey}, "https://idp.example.test")
verifier := oidc.NewVerifier(map[string]*rsa.PublicKey{"trusted-kid": &trustedKey.PublicKey}, "https://idp.example.test", "https://nexarch.example.test/tenants/pentest")
claims := jwt.MapClaims{
"sub": "angreifer@example.test",
@@ -177,11 +177,12 @@ func TestPentest_OIDC_TamperedClaims(t *testing.T) {
if err != nil {
t.Fatalf("rsa key: %v", err)
}
verifier := oidc.NewVerifier(map[string]*rsa.PublicKey{"kid-1": &rsaKey.PublicKey}, "https://idp.example.test")
verifier := oidc.NewVerifier(map[string]*rsa.PublicKey{"kid-1": &rsaKey.PublicKey}, "https://idp.example.test", "https://nexarch.example.test/tenants/pentest")
claims := jwt.MapClaims{
"sub": "normaler-benutzer@example.test",
"iss": "https://idp.example.test",
"aud": "https://nexarch.example.test/tenants/pentest",
"roles": []string{"user"},
"exp": time.Now().Add(time.Hour).Unix(),
}
@@ -206,6 +207,7 @@ func TestPentest_OIDC_TamperedClaims(t *testing.T) {
tamperedClaims := jwt.MapClaims{
"sub": "normaler-benutzer@example.test",
"iss": "https://idp.example.test",
"aud": "https://nexarch.example.test/tenants/pentest",
"roles": []string{"superadmin"}, // Rechteausweitung
"exp": time.Now().Add(time.Hour).Unix(),
}
@@ -232,11 +234,12 @@ func TestPentest_OIDC_NonceReplay(t *testing.T) {
if err != nil {
t.Fatalf("rsa key: %v", err)
}
verifier := oidc.NewVerifier(map[string]*rsa.PublicKey{"kid-1": &rsaKey.PublicKey}, "https://idp.example.test")
verifier := oidc.NewVerifier(map[string]*rsa.PublicKey{"kid-1": &rsaKey.PublicKey}, "https://idp.example.test", "https://nexarch.example.test/tenants/pentest")
claims := jwt.MapClaims{
"sub": "benutzer@example.test",
"iss": "https://idp.example.test",
"aud": "https://nexarch.example.test/tenants/pentest",
"nonce": "nonce-aus-login-versuch-1",
"exp": time.Now().Add(time.Hour).Unix(),
}
+2 -2
View File
@@ -182,7 +182,7 @@ func TestSAMLAndOIDC_WorkInParallelForDifferentTenants(t *testing.T) {
// Tenant "oidc-tenant": OIDC konfiguriert, voellig unabhaengige Datenbank.
oidcKey := generateTestIdPKey(t)
oidcVerifier := oidc.NewVerifier(map[string]*rsa.PublicKey{"kid-1": &oidcKey.PublicKey}, "https://oidc-idp.example.com")
oidcVerifier := oidc.NewVerifier(map[string]*rsa.PublicKey{"kid-1": &oidcKey.PublicKey}, "https://oidc-idp.example.com", "https://nexarch.example.com/tenants/oidc-tenant")
oidcStates := oidc.NewStateStore(poolOIDC)
oidcUsers := user.NewTenantUserStore(poolOIDC)
oidcRoles := rbac.NewStore(poolOIDC)
@@ -197,7 +197,7 @@ func TestSAMLAndOIDC_WorkInParallelForDifferentTenants(t *testing.T) {
Email: oidcEmail,
Nonce: nonce,
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "https://oidc-idp.example.com", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
Issuer: "https://oidc-idp.example.com", Audience: jwt.ClaimStrings{"https://nexarch.example.com/tenants/oidc-tenant"}, ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
},
}
idToken := signOIDCTestToken(t, oidcKey, claims)