test: 固化 OIDC 校验错误边界

This commit is contained in:
2026-07-14 11:11:48 +08:00
parent dd1ddd6ead
commit 39b6da0ada
2 changed files with 63 additions and 31 deletions
@@ -8,6 +8,7 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"io"
"net/http"
"net/http/httptest"
@@ -132,6 +133,24 @@ func TestOIDCPublicClientVerifiesIDTokenNonceAndAudience(t *testing.T) {
if _, err := client.VerifyIDToken(context.Background(), idToken, "wrong-nonce"); err == nil {
t.Fatal("ID token with mismatched nonce was accepted")
}
for _, test := range []struct {
name string
mutate func(jwt.MapClaims)
}{
{name: "wrong audience", mutate: func(claims jwt.MapClaims) { claims["aud"] = "other-client" }},
{name: "missing nbf", mutate: func(claims jwt.MapClaims) { delete(claims, "nbf") }},
} {
t.Run(test.name, func(t *testing.T) {
invalid := signedOIDCToken(t, issuer, "ec-key", jwt.SigningMethodES256, key, func(claims jwt.MapClaims) {
claims["aud"] = "gateway-public-client"
claims["nonce"] = "expected-nonce"
test.mutate(claims)
})
if _, err := client.VerifyIDToken(context.Background(), invalid, "expected-nonce"); err == nil {
t.Fatal("invalid ID token was accepted")
}
})
}
}
func TestOIDCPublicClientRefreshAndRevokeNeverSendSecret(t *testing.T) {
@@ -189,3 +208,37 @@ func TestOIDCPublicClientRefreshAndRevokeNeverSendSecret(t *testing.T) {
t.Fatalf("requests = %d, want 2", requests)
}
}
func TestOIDCPublicClientMapsInvalidGrantWithoutLeakingProviderResponse(t *testing.T) {
var issuer string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/.well-known/openid-configuration":
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]string{
"issuer": issuer, "authorization_endpoint": issuer + "/authorize",
"token_endpoint": issuer + "/token", "jwks_uri": issuer + "/jwks",
})
case "/token":
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(`{"error":"invalid_grant","error_description":"sensitive-provider-detail"}`))
default:
http.NotFound(w, r)
}
}))
defer server.Close()
issuer = server.URL
client, err := NewOIDCPublicClient(OIDCPublicClientConfig{
Issuer: issuer, ClientID: "gateway-public", RedirectURI: "https://gateway.example.com/callback",
PostLogoutRedirectURI: "https://gateway.example.com/", HTTPClient: server.Client(),
})
if err != nil {
t.Fatal(err)
}
_, err = client.Refresh(context.Background(), "redacted-refresh-token")
if !errors.Is(err, ErrOIDCInvalidGrant) || strings.Contains(err.Error(), "sensitive-provider-detail") {
t.Fatalf("invalid_grant mapping was not stable and redacted: %v", err)
}
}