fix(identity): 完善统一认证配对恢复与安全退役
修复 credentials_saved 状态无法恢复、配对与激活并发冲突,以及 SSF 和身份 Secret 生命周期不完整的问题。新增持久化协调器、取消与清理状态机、事务级并发门禁、受控 SSF 凭据交接、禁用后的延迟 Secret 清理,并对生产环境统一认证及 Discovery 端点强制 HTTPS。 验证:go test ./...;go test -race ./internal/auth ./internal/identity ./internal/identityruntime ./internal/securityevents ./internal/httpapi ./internal/store -count=1;go vet ./...;真实 PostgreSQL 并发及清理成功/冲突回滚测试;pnpm openapi。
This commit is contained in:
@@ -57,6 +57,7 @@ func TestOIDCPublicClientUsesAuthorizationCodePKCES256WithoutSecret(t *testing.T
|
||||
issuer = server.URL
|
||||
|
||||
client, err := NewOIDCPublicClient(OIDCPublicClientConfig{
|
||||
AppEnv: "test",
|
||||
Issuer: issuer, ClientID: "gateway-public", RedirectURI: "https://gateway.example.com/gateway-api/api/v1/auth/oidc/callback",
|
||||
PostLogoutRedirectURI: "https://gateway.example.com/", Scopes: []string{"openid", "profile", "gateway.access"}, HTTPClient: server.Client(),
|
||||
})
|
||||
@@ -84,6 +85,7 @@ func TestOIDCPublicClientUsesAuthorizationCodePKCES256WithoutSecret(t *testing.T
|
||||
|
||||
func TestOIDCPublicClientRejectsOfflineAccess(t *testing.T) {
|
||||
_, err := NewOIDCPublicClient(OIDCPublicClientConfig{
|
||||
AppEnv: "production",
|
||||
Issuer: "https://auth.example.com", ClientID: "gateway-public",
|
||||
RedirectURI: "https://gateway.example.com/api/v1/auth/oidc/callback",
|
||||
PostLogoutRedirectURI: "https://gateway.example.com/",
|
||||
@@ -119,6 +121,7 @@ func TestOIDCPublicClientVerifiesIDTokenNonceAndAudience(t *testing.T) {
|
||||
issuer = server.URL
|
||||
|
||||
client, err := NewOIDCPublicClient(OIDCPublicClientConfig{
|
||||
AppEnv: "test",
|
||||
Issuer: issuer, ClientID: "gateway-public-client", RedirectURI: "https://gateway.example.com/callback",
|
||||
PostLogoutRedirectURI: "https://gateway.example.com/", HTTPClient: server.Client(),
|
||||
})
|
||||
@@ -195,6 +198,7 @@ func TestOIDCPublicClientRefreshAndRevokeNeverSendSecret(t *testing.T) {
|
||||
defer server.Close()
|
||||
issuer = server.URL
|
||||
client, err := NewOIDCPublicClient(OIDCPublicClientConfig{
|
||||
AppEnv: "test",
|
||||
Issuer: issuer, ClientID: "gateway-public", RedirectURI: "https://gateway.example.com/callback",
|
||||
PostLogoutRedirectURI: "https://gateway.example.com/", HTTPClient: server.Client(),
|
||||
})
|
||||
@@ -234,6 +238,7 @@ func TestOIDCPublicClientMapsInvalidGrantWithoutLeakingProviderResponse(t *testi
|
||||
issuer = server.URL
|
||||
|
||||
client, err := NewOIDCPublicClient(OIDCPublicClientConfig{
|
||||
AppEnv: "test",
|
||||
Issuer: issuer, ClientID: "gateway-public", RedirectURI: "https://gateway.example.com/callback",
|
||||
PostLogoutRedirectURI: "https://gateway.example.com/", HTTPClient: server.Client(),
|
||||
})
|
||||
@@ -245,3 +250,43 @@ func TestOIDCPublicClientMapsInvalidGrantWithoutLeakingProviderResponse(t *testi
|
||||
t.Fatalf("invalid_grant mapping was not stable and redacted: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOIDCPublicClientRejectsLoopbackHTTPDiscoveryEndpointsInProduction(t *testing.T) {
|
||||
var issuer, insecureField string
|
||||
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if r.URL.Path != "/.well-known/openid-configuration" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
metadata := map[string]any{
|
||||
"issuer": issuer, "authorization_endpoint": issuer + "/authorize",
|
||||
"token_endpoint": issuer + "/token", "jwks_uri": issuer + "/jwks",
|
||||
"revocation_endpoint": issuer + "/revoke", "end_session_endpoint": issuer + "/logout",
|
||||
"id_token_signing_alg_values_supported": []string{"RS256", "ES256"},
|
||||
}
|
||||
metadata[insecureField] = "http://127.0.0.1:1/oidc-endpoint"
|
||||
_ = json.NewEncoder(w).Encode(metadata)
|
||||
}))
|
||||
defer server.Close()
|
||||
issuer = server.URL
|
||||
|
||||
for _, field := range []string{
|
||||
"authorization_endpoint", "token_endpoint", "jwks_uri", "revocation_endpoint", "end_session_endpoint",
|
||||
} {
|
||||
t.Run(field, func(t *testing.T) {
|
||||
insecureField = field
|
||||
client, err := NewOIDCPublicClient(OIDCPublicClientConfig{
|
||||
AppEnv: "production", 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)
|
||||
}
|
||||
if err := client.ValidateConfiguration(context.Background()); err == nil {
|
||||
t.Fatalf("production accepted loopback HTTP %s", field)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user