feat: 实现 OIDC 服务端会话与请求刷新

使用 AES-256-GCM 保存认证中心令牌,并以随机 Cookie 哈希关联 PostgreSQL 会话。加入闲置与绝对期限、Refresh Token 轮换以及多实例刷新锁。
This commit is contained in:
2026-07-13 19:09:10 +08:00
parent a81a7b5200
commit d345c070ae
16 changed files with 1804 additions and 58 deletions
+29
View File
@@ -104,6 +104,35 @@ func TestOIDCVerifierRejectsMissingOrMismatchedSecurityClaims(t *testing.T) {
}
}
func TestOIDCVerifierValidatesIDTokenNonceAndPublicClientAudience(t *testing.T) {
key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
var issuer string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) {
if request.URL.Path == "/.well-known/openid-configuration" {
_ = json.NewEncoder(w).Encode(map[string]any{"issuer": issuer, "jwks_uri": issuer + "/jwks"})
return
}
_ = json.NewEncoder(w).Encode(map[string]any{"keys": []any{ecJWK("ec-key", &key.PublicKey)}})
}))
defer server.Close()
issuer = server.URL
verifier, _ := NewOIDCVerifier(OIDCConfig{
Issuer: issuer, Audience: "gateway-api", TenantID: "tenant-1", RolePrefix: "gateway.",
RequiredScopes: []string{"gateway.access"}, HTTPClient: server.Client(),
})
idToken := signedOIDCToken(t, issuer, "ec-key", jwt.SigningMethodES256, key, func(claims jwt.MapClaims) {
claims["aud"] = "gateway-public-client"
claims["nonce"] = "expected-nonce"
})
subject, err := verifier.VerifyIDToken(context.Background(), idToken, "gateway-public-client", "expected-nonce")
if err != nil || subject != "platform-subject" {
t.Fatalf("VerifyIDToken() subject=%q err=%v", subject, err)
}
if _, err := verifier.VerifyIDToken(context.Background(), idToken, "gateway-public-client", "wrong-nonce"); err == nil {
t.Fatal("ID token with mismatched nonce was accepted")
}
}
func TestOIDCVerifierFailsClosedWhenIntrospectionMarksSessionInactive(t *testing.T) {
key, _ := rsa.GenerateKey(rand.Reader, 2048)
active := true