feat: 实现 OIDC 服务端会话与请求刷新
使用 AES-256-GCM 保存认证中心令牌,并以随机 Cookie 哈希关联 PostgreSQL 会话。加入闲置与绝对期限、Refresh Token 轮换以及多实例刷新锁。
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -63,11 +65,18 @@ func TestLoadOIDCBrowserSessionUsesSafeEnvironmentDefaults(t *testing.T) {
|
||||
|
||||
func TestValidateRejectsInsecureNonLocalOIDCBrowserSession(t *testing.T) {
|
||||
cfg := Config{
|
||||
AppEnv: "staging",
|
||||
OIDCEnabled: true,
|
||||
OIDCBrowserSessionEnabled: true,
|
||||
OIDCSessionCookieSecure: false,
|
||||
CORSAllowedOrigin: "https://gateway.example.com",
|
||||
AppEnv: "staging",
|
||||
OIDCEnabled: true,
|
||||
OIDCBrowserSessionEnabled: true,
|
||||
OIDCSessionCookieSecure: false,
|
||||
CORSAllowedOrigin: "https://gateway.example.com",
|
||||
OIDCClientID: "gateway-public",
|
||||
OIDCRedirectURI: "https://gateway.example.com/gateway-api/api/v1/auth/oidc/callback",
|
||||
OIDCPostLogoutRedirectURI: "https://gateway.example.com/",
|
||||
OIDCSessionEncryptionKey: base64.RawStdEncoding.EncodeToString(bytes.Repeat([]byte{1}, 32)),
|
||||
OIDCSessionIdleTTLSeconds: 1800,
|
||||
OIDCSessionAbsoluteTTLSeconds: 28800,
|
||||
OIDCSessionRefreshBeforeSeconds: 60,
|
||||
}
|
||||
if err := cfg.Validate(); err == nil || !strings.Contains(err.Error(), "OIDC_SESSION_COOKIE_SECURE") {
|
||||
t.Fatalf("Validate() error = %v, want insecure non-local cookie rejection", err)
|
||||
@@ -79,3 +88,51 @@ func TestValidateRejectsInsecureNonLocalOIDCBrowserSession(t *testing.T) {
|
||||
t.Fatalf("Validate() error = %v, want wildcard credentialed CORS rejection", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRequiresCompletePublicClientSessionConfiguration(t *testing.T) {
|
||||
cfg := Config{
|
||||
AppEnv: "test", OIDCEnabled: true, OIDCBrowserSessionEnabled: true,
|
||||
OIDCSessionCookieSecure: false, CORSAllowedOrigin: "http://localhost:5178",
|
||||
}
|
||||
if err := cfg.Validate(); err == nil || !strings.Contains(err.Error(), "OIDC_CLIENT_ID") {
|
||||
t.Fatalf("Validate() error = %v, want incomplete public client rejection", err)
|
||||
}
|
||||
|
||||
cfg.OIDCClientID = "gateway-public"
|
||||
cfg.OIDCRedirectURI = "http://localhost:8088/api/v1/auth/oidc/callback"
|
||||
cfg.OIDCPostLogoutRedirectURI = "http://localhost:5178/"
|
||||
cfg.OIDCSessionEncryptionKey = base64.RawStdEncoding.EncodeToString(bytes.Repeat([]byte{2}, 32))
|
||||
cfg.OIDCSessionIdleTTLSeconds = 1800
|
||||
cfg.OIDCSessionAbsoluteTTLSeconds = 28800
|
||||
cfg.OIDCSessionRefreshBeforeSeconds = 60
|
||||
if err := cfg.Validate(); err != nil {
|
||||
t.Fatalf("Validate() valid public session config: %v", err)
|
||||
}
|
||||
key, err := cfg.OIDCSessionEncryptionKeyBytes()
|
||||
if err != nil || len(key) != 32 {
|
||||
t.Fatalf("decoded encryption key len=%d err=%v", len(key), err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRejectsPlaintextOrWrongLengthSessionKey(t *testing.T) {
|
||||
cfg := Config{
|
||||
AppEnv: "test", OIDCEnabled: true, OIDCBrowserSessionEnabled: true,
|
||||
OIDCClientID: "gateway-public", OIDCRedirectURI: "http://localhost:8088/callback",
|
||||
OIDCPostLogoutRedirectURI: "http://localhost:5178/", OIDCSessionEncryptionKey: strings.Repeat("x", 32),
|
||||
OIDCSessionIdleTTLSeconds: 1800, OIDCSessionAbsoluteTTLSeconds: 28800, OIDCSessionRefreshBeforeSeconds: 60,
|
||||
CORSAllowedOrigin: "http://localhost:5178",
|
||||
}
|
||||
if err := cfg.Validate(); err == nil || !strings.Contains(err.Error(), "OIDC_SESSION_ENCRYPTION_KEY") {
|
||||
t.Fatalf("Validate() error = %v, want encoded AES-256 key rejection", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRejectsOfflineAccessForBrowserSession(t *testing.T) {
|
||||
cfg := Config{
|
||||
AppEnv: "test", OIDCEnabled: true, OIDCBrowserSessionEnabled: true,
|
||||
OIDCRequiredScopes: []string{"gateway.access", "offline_access"},
|
||||
}
|
||||
if err := cfg.Validate(); err == nil || !strings.Contains(err.Error(), "offline_access") {
|
||||
t.Fatalf("Validate() error = %v, want offline_access rejection", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user