package identity import ( "context" "crypto/rand" "crypto/rsa" "encoding/json" "net/http" "net/http/httptest" "testing" "time" "github.com/go-jose/go-jose/v4" josejwt "github.com/go-jose/go-jose/v4/jwt" ) func TestOIDCVerifierAcceptsSignedToken(t *testing.T) { privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatal(err) } keyID := "test-key" webKey := jose.JSONWebKey{Key: &privateKey.PublicKey, KeyID: keyID, Algorithm: string(jose.RS256), Use: "sig"} keySet := jose.JSONWebKeySet{Keys: []jose.JSONWebKey{webKey}} server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) { writer.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(writer).Encode(keySet) })) defer server.Close() issuer := "https://identity.example.test/realms/test" audience := "voice-platform-web" rawToken := signTestToken(t, privateKey, keyID, issuer, audience) verifier := NewOIDCVerifier(context.Background(), issuer, server.URL, audience) principal, err := verifier.Verify(context.Background(), rawToken) if err != nil { t.Fatalf("verify token: %v", err) } if principal.Issuer != issuer || principal.Subject != "user-123" || principal.PreferredUsername != "tester" { t.Fatalf("unexpected principal: %#v", principal) } } func TestOIDCVerifierRejectsEmptyAndWrongAudience(t *testing.T) { verifier := &OIDCVerifier{} if _, err := verifier.Verify(context.Background(), ""); err != ErrInvalidToken { t.Fatalf("empty token error = %v", err) } privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatal(err) } keyID := "test-key" keySet := jose.JSONWebKeySet{Keys: []jose.JSONWebKey{{ Key: &privateKey.PublicKey, KeyID: keyID, Algorithm: string(jose.RS256), Use: "sig", }}} server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) { _ = json.NewEncoder(writer).Encode(keySet) })) defer server.Close() issuer := "https://identity.example.test/realms/test" verifier = NewOIDCVerifier(context.Background(), issuer, server.URL, "expected-audience") if _, err := verifier.Verify(context.Background(), signTestToken(t, privateKey, keyID, issuer, "wrong-audience")); err != ErrInvalidToken { t.Fatalf("wrong audience error = %v", err) } } func signTestToken(t *testing.T, key *rsa.PrivateKey, keyID, issuer, audience string) string { t.Helper() options := (&jose.SignerOptions{}).WithType("JWT").WithHeader("kid", keyID) signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.RS256, Key: key}, options) if err != nil { t.Fatal(err) } rawToken, err := josejwt.Signed(signer). Claims(josejwt.Claims{ Issuer: issuer, Subject: "user-123", Audience: josejwt.Audience{audience}, Expiry: josejwt.NewNumericDate(time.Now().Add(time.Minute)), IssuedAt: josejwt.NewNumericDate(time.Now()), }). Claims(map[string]any{"name": "测试用户", "preferred_username": "tester"}). Serialize() if err != nil { t.Fatal(err) } return rawToken }