feat: 接入 SSF 实时会话撤销
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"crypto/rsa"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -153,6 +154,59 @@ func TestOIDCVerifierFailsClosedWhenIntrospectionMarksSessionInactive(t *testing
|
||||
}
|
||||
}
|
||||
|
||||
func TestOIDCSecurityEventEvaluationUsesWatermarkAndFallbackIntrospection(t *testing.T) {
|
||||
key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
active, introspectionAvailable, introspectionCalls := true, true, 0
|
||||
var issuer string
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) {
|
||||
switch request.URL.Path {
|
||||
case "/.well-known/openid-configuration":
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"issuer": issuer, "jwks_uri": issuer + "/jwks", "introspection_endpoint": issuer + "/introspect"})
|
||||
case "/jwks":
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"keys": []any{ecJWK("ec-key", &key.PublicKey)}})
|
||||
case "/introspect":
|
||||
introspectionCalls++
|
||||
if !introspectionAvailable {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"active": active})
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
issuer = server.URL
|
||||
evaluation := OIDCSecurityEventEvaluation{RequireIntrospection: true}
|
||||
verifier, err := NewOIDCVerifier(OIDCConfig{
|
||||
Issuer: issuer, Audience: "gateway-api", TenantID: "tenant-1", RolePrefix: "gateway.",
|
||||
RequiredScopes: []string{"gateway.access"}, HTTPClient: server.Client(),
|
||||
IntrospectionClientID: "gateway-introspection", IntrospectionClientSecret: "introspection-secret",
|
||||
SecurityEventEvaluator: func(_ context.Context, identity OIDCSecurityEventIdentity) (OIDCSecurityEventEvaluation, error) {
|
||||
if identity.Subject != "platform-subject" || identity.IssuedAt.IsZero() {
|
||||
t.Fatal("security event evaluator received incomplete identity")
|
||||
}
|
||||
return evaluation, nil
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
raw := signedOIDCToken(t, issuer, "ec-key", jwt.SigningMethodES256, key, nil)
|
||||
if _, err := verifier.Verify(context.Background(), raw); err != nil || introspectionCalls != 1 {
|
||||
t.Fatalf("fallback token error=%v calls=%d", err, introspectionCalls)
|
||||
}
|
||||
evaluation = OIDCSecurityEventEvaluation{Revoked: true}
|
||||
if _, err := verifier.Verify(context.Background(), raw); err == nil || introspectionCalls != 1 {
|
||||
t.Fatalf("revoked token error=%v calls=%d", err, introspectionCalls)
|
||||
}
|
||||
evaluation = OIDCSecurityEventEvaluation{RequireIntrospection: true}
|
||||
introspectionAvailable = false
|
||||
_, err = verifier.Verify(context.Background(), raw)
|
||||
var requestError *RequestAuthError
|
||||
if !errors.As(err, &requestError) || requestError.Status != http.StatusServiceUnavailable {
|
||||
t.Fatalf("introspection outage error=%T %v", err, err)
|
||||
}
|
||||
}
|
||||
|
||||
func signedOIDCToken(t *testing.T, issuer, kid string, method jwt.SigningMethod, key any, mutate func(jwt.MapClaims)) string {
|
||||
t.Helper()
|
||||
now := time.Now()
|
||||
|
||||
Reference in New Issue
Block a user