package config import ( "bytes" "encoding/base64" "strings" "testing" ) func TestLoadOIDCJITProvisioningDefaultsToDisabled(t *testing.T) { t.Setenv("OIDC_JIT_PROVISIONING_ENABLED", "") t.Setenv("OIDC_GATEWAY_TENANT_KEY", "") cfg := Load() if cfg.OIDCJITProvisioningEnabled { t.Fatal("OIDC JIT provisioning must be disabled by default") } if cfg.OIDCGatewayTenantKey != "" { t.Fatalf("unexpected gateway tenant key: %q", cfg.OIDCGatewayTenantKey) } } func TestValidateRequiresGatewayTenantKeyWhenOIDCJITIsEnabled(t *testing.T) { cfg := Config{ OIDCEnabled: true, OIDCJITProvisioningEnabled: true, } err := cfg.Validate() if err == nil || !strings.Contains(err.Error(), "OIDC_GATEWAY_TENANT_KEY") { t.Fatalf("Validate() error = %v, want missing OIDC_GATEWAY_TENANT_KEY", err) } cfg.OIDCGatewayTenantKey = "default" if err := cfg.Validate(); err != nil { t.Fatalf("Validate() with tenant key: %v", err) } } func TestLoadOIDCBrowserSessionUsesSafeEnvironmentDefaults(t *testing.T) { t.Setenv("APP_ENV", "development") t.Setenv("OIDC_BROWSER_SESSION_ENABLED", "") t.Setenv("OIDC_SESSION_COOKIE_SECURE", "") cfg := Load() if !cfg.OIDCBrowserSessionEnabled { t.Fatal("OIDC browser session should be enabled by default") } if cfg.OIDCSessionCookieSecure { t.Fatal("development cookie should allow localhost HTTP by default") } t.Setenv("APP_ENV", "production") cfg = Load() if !cfg.OIDCSessionCookieSecure { t.Fatal("production OIDC session cookie must default to Secure") } t.Setenv("APP_ENV", "staging") cfg = Load() if !cfg.OIDCSessionCookieSecure { t.Fatal("staging OIDC session cookie must default to Secure") } } func TestValidateRejectsInsecureNonLocalOIDCBrowserSession(t *testing.T) { cfg := Config{ 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) } cfg.OIDCSessionCookieSecure = true cfg.CORSAllowedOrigin = "*" if err := cfg.Validate(); err == nil || !strings.Contains(err.Error(), "CORS_ALLOWED_ORIGIN") { 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) } }