package oidc import ( "crypto/rsa" "errors" "fmt" "github.com/golang-jwt/jwt/v5" ) var ErrInvalidToken = errors.New("oidc: id-token ungueltig, abgelaufen oder falscher aussteller") // Claims sind die fuer NEXARCH relevanten OIDC-ID-Token-Claims. type Claims struct { Subject string `json:"sub"` Email string `json:"email"` Nonce string `json:"nonce"` Roles []string `json:"roles"` // providerabhaengiger Claim-Name, hier vereinfacht fest "roles" jwt.RegisteredClaims } // Verifier prueft ID-Tokens gegen den JWKS-Schluesselsatz eines konfigurierten // Providers (Akzeptanzkriterium 3: fehlerhafte/abgelaufene Tokens werden // abgewiesen). type Verifier struct { keys map[string]*rsa.PublicKey issuer string } func NewVerifier(keys map[string]*rsa.PublicKey, issuer string) *Verifier { return &Verifier{keys: keys, issuer: issuer} } // Verify prueft Signatur (gegen JWKS), Ablauf (jwt-Bibliothek lehnt // abgelaufene Tokens automatisch ab) und Aussteller. 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 } kid, _ := t.Header["kid"].(string) key, ok := v.keys[kid] if !ok { return nil, ErrInvalidToken } return key, nil }) if err != nil || !token.Valid { return nil, ErrInvalidToken } if claims.Issuer != v.issuer { return nil, fmt.Errorf("%w: unerwarteter aussteller %q", ErrInvalidToken, claims.Issuer) } return claims, nil } // 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). func (v *Verifier) VerifyWithNonce(idToken, expectedNonce string) (*Claims, error) { claims, err := v.Verify(idToken) if err != nil { return nil, err } if claims.Nonce == "" || claims.Nonce != expectedNonce { return nil, fmt.Errorf("%w: nonce stimmt nicht ueberein", ErrInvalidToken) } return claims, nil }