package config import ( "net/http" "net/url" "strings" ) // getCookieSecure détermine si les cookies doivent être Secure // Auto-detect: secure en production, insecure en développement // Peut être forcé via COOKIE_SECURE=true/false func getCookieSecure(env string) bool { cookieSecureEnv := getEnv("COOKIE_SECURE", "") if cookieSecureEnv != "" { return getEnvBool("COOKIE_SECURE", false) } // Auto-detect: secure en production, insecure en développement return (env == EnvProduction) } // getCookieSameSite détermine la politique SameSite pour les cookies // strict par défaut pour sécurité maximale, lax en développement local func getCookieSameSite(env string) string { cookieSameSite := getEnv("COOKIE_SAME_SITE", "strict") if env == EnvDevelopment && cookieSameSite == "strict" { // En dev local, utiliser "lax" pour permettre le domaine local (APP_DOMAIN) return "lax" } return cookieSameSite } // GetCookieSameSite retourne la valeur http.SameSite correspondante func (c *Config) GetCookieSameSite() http.SameSite { switch c.CookieSameSite { case "lax": return http.SameSiteLaxMode case "none": return http.SameSiteNoneMode default: return http.SameSiteStrictMode } } // ShouldUseSecureCookies détermine si les cookies doivent être Secure // Prend en compte la configuration explicite et l'environnement func (c *Config) ShouldUseSecureCookies() bool { return c.CookieSecure } // devDefaultCORSOrigins returns origins always allowed in development/staging. // Generated from APP_DOMAIN so that changing the domain propagates everywhere. func devDefaultCORSOrigins(appDomain string) []string { return []string{ "http://" + appDomain, "http://" + appDomain + ":3000", "http://" + appDomain + ":5173", "http://" + appDomain + ":8080", } } // mergeCORSOrigins merges custom with base and deduplicates (order: base then custom). func mergeCORSOrigins(base, custom []string) []string { seen := make(map[string]bool) out := make([]string, 0, len(base)+len(custom)) for _, o := range base { if !seen[o] { seen[o] = true out = append(out, o) } } for _, o := range custom { if !seen[o] { seen[o] = true out = append(out, o) } } return out } // getCORSOrigins charge les origines CORS avec defaults sécurisés selon l'environnement (P0-SECURITY) // - development/staging: si CORS_ALLOWED_ORIGINS est défini, on le fusionne avec les defaults (APP_DOMAIN) // pour que $APP_DOMAIN:5173 reste autorisé même avec une config partielle // - production: CORS_ALLOWED_ORIGINS obligatoire, pas de merge // - test: liste vide par défaut func getCORSOrigins(env string, appDomain string) []string { custom := getEnvStringSlice("CORS_ALLOWED_ORIGINS", nil) switch env { case EnvProduction: if len(custom) > 0 { return custom } return []string{} case EnvTest: if len(custom) > 0 { return custom } return []string{} case EnvDevelopment, EnvStaging: base := devDefaultCORSOrigins(appDomain) if len(custom) > 0 { return mergeCORSOrigins(base, custom) } return base default: return []string{"http://" + appDomain + ":3000", "http://" + appDomain + ":5173"} } } // getOAuthAllowedRedirectDomains returns the whitelist of domains allowed for OAuth redirect (v0.902). // If OAUTH_ALLOWED_REDIRECT_DOMAINS is set, use it. Otherwise derive from CORSOrigins or FrontendURL. func getOAuthAllowedRedirectDomains(env string, explicit []string, corsOrigins []string, frontendURL string) []string { if len(explicit) > 0 { return explicit } if len(corsOrigins) > 0 { return corsOrigins } if frontendURL != "" { if u, err := url.Parse(frontendURL); err == nil { origin := strings.TrimSuffix(u.String(), "/") if u.Scheme != "" && u.Host != "" { return []string{origin} } } } // Dev fallback if env == EnvDevelopment || env == EnvStaging { return []string{"http://localhost:5173", "http://localhost:3000", "http://127.0.0.1:5173", "http://127.0.0.1:3000"} } return []string{} }