From 52b45d9b3da01bd47428adc6cd4edf6604a11528 Mon Sep 17 00:00:00 2001 From: zhx Date: Thu, 4 Jun 2026 15:32:43 +0800 Subject: [PATCH] fix: configure admin refresh cookie same site --- .../admin/configs/config.tencent.example.yaml | 1 + server/admin/configs/config.yaml | 1 + server/admin/internal/config/config.go | 68 +++++++++++-------- server/admin/internal/modules/auth/handler.go | 14 +++- 4 files changed, 56 insertions(+), 28 deletions(-) diff --git a/server/admin/configs/config.tencent.example.yaml b/server/admin/configs/config.tencent.example.yaml index 4acfddce..78edae8a 100644 --- a/server/admin/configs/config.tencent.example.yaml +++ b/server/admin/configs/config.tencent.example.yaml @@ -25,6 +25,7 @@ cors_allowed_origins: - "http://hyapp-platform.global-interaction.com" - "https://hyapp-platform.global-interaction.com" refresh_cookie_secure: true +refresh_cookie_same_site: "none" bootstrap: # 仅首次初始化或显式 -bootstrap 时打开;生产常驻进程不要每次启动重放 seed。 enabled: false diff --git a/server/admin/configs/config.yaml b/server/admin/configs/config.yaml index 26d9be1e..9b4ac1db 100644 --- a/server/admin/configs/config.yaml +++ b/server/admin/configs/config.yaml @@ -26,6 +26,7 @@ cors_allowed_origins: - "http://localhost:7002" - "http://127.0.0.1:7002" refresh_cookie_secure: false +refresh_cookie_same_site: "lax" bootstrap: enabled: true seed_demo_data: true diff --git a/server/admin/internal/config/config.go b/server/admin/internal/config/config.go index 5cc4a248..b0595e31 100644 --- a/server/admin/internal/config/config.go +++ b/server/admin/internal/config/config.go @@ -13,32 +13,33 @@ import ( ) type Config struct { - ServiceName string `yaml:"service_name"` - NodeID string `yaml:"node_id"` - Environment string `yaml:"environment"` - Log logging.Config `yaml:"log"` - HTTPAddr string `yaml:"http_addr"` - MySQLDSN string `yaml:"mysql_dsn"` - UserMySQLDSN string `yaml:"user_mysql_dsn"` - WalletMySQLDSN string `yaml:"wallet_mysql_dsn"` - MySQLAutoMigrate bool `yaml:"mysql_auto_migrate"` - Migrations MigrationConfig `yaml:"migrations"` - JWTSecret string `yaml:"jwt_secret"` - AccessTokenTTL time.Duration `yaml:"access_token_ttl"` - RefreshTokenTTL time.Duration `yaml:"refresh_token_ttl"` - CORSAllowedOrigins []string `yaml:"cors_allowed_origins"` - RefreshCookieSecure bool `yaml:"refresh_cookie_secure"` - Bootstrap BootstrapConfig `yaml:"bootstrap"` - BootstrapPassword string `yaml:"bootstrap_password"` - UserService UserServiceConfig `yaml:"user_service"` - TencentCOS TencentCOSConfig `yaml:"tencent-cos"` - Redis RedisConfig `yaml:"redis"` - Jobs JobsConfig `yaml:"jobs"` - WalletService WalletServiceConfig `yaml:"wallet_service"` - RoomService RoomServiceConfig `yaml:"room_service"` - ActivityService ActivityServiceConfig `yaml:"activity_service"` - GameService GameServiceConfig `yaml:"game_service"` - StatisticsService StatisticsServiceConfig `yaml:"statistics_service"` + ServiceName string `yaml:"service_name"` + NodeID string `yaml:"node_id"` + Environment string `yaml:"environment"` + Log logging.Config `yaml:"log"` + HTTPAddr string `yaml:"http_addr"` + MySQLDSN string `yaml:"mysql_dsn"` + UserMySQLDSN string `yaml:"user_mysql_dsn"` + WalletMySQLDSN string `yaml:"wallet_mysql_dsn"` + MySQLAutoMigrate bool `yaml:"mysql_auto_migrate"` + Migrations MigrationConfig `yaml:"migrations"` + JWTSecret string `yaml:"jwt_secret"` + AccessTokenTTL time.Duration `yaml:"access_token_ttl"` + RefreshTokenTTL time.Duration `yaml:"refresh_token_ttl"` + CORSAllowedOrigins []string `yaml:"cors_allowed_origins"` + RefreshCookieSecure bool `yaml:"refresh_cookie_secure"` + RefreshCookieSameSite string `yaml:"refresh_cookie_same_site"` + Bootstrap BootstrapConfig `yaml:"bootstrap"` + BootstrapPassword string `yaml:"bootstrap_password"` + UserService UserServiceConfig `yaml:"user_service"` + TencentCOS TencentCOSConfig `yaml:"tencent-cos"` + Redis RedisConfig `yaml:"redis"` + Jobs JobsConfig `yaml:"jobs"` + WalletService WalletServiceConfig `yaml:"wallet_service"` + RoomService RoomServiceConfig `yaml:"room_service"` + ActivityService ActivityServiceConfig `yaml:"activity_service"` + GameService GameServiceConfig `yaml:"game_service"` + StatisticsService StatisticsServiceConfig `yaml:"statistics_service"` } type MigrationConfig struct { @@ -135,7 +136,8 @@ func Default() Config { "http://localhost:7001", "http://127.0.0.1:7001", }, - RefreshCookieSecure: false, + RefreshCookieSecure: false, + RefreshCookieSameSite: "lax", Bootstrap: BootstrapConfig{ Enabled: true, SeedDemoData: true, @@ -225,6 +227,10 @@ func (cfg *Config) Normalize() { if cfg.Log.MaxPayloadBytes <= 0 { cfg.Log.MaxPayloadBytes = 2048 } + cfg.RefreshCookieSameSite = strings.ToLower(strings.TrimSpace(cfg.RefreshCookieSameSite)) + if cfg.RefreshCookieSameSite == "" { + cfg.RefreshCookieSameSite = "lax" + } cfg.ServiceName = strings.TrimSpace(cfg.ServiceName) cfg.NodeID = strings.TrimSpace(cfg.NodeID) if cfg.NodeID == "" { @@ -332,6 +338,14 @@ func (cfg Config) Validate() error { if cfg.RefreshTokenTTL <= 0 { return errors.New("refresh_token_ttl must be greater than 0") } + switch strings.ToLower(strings.TrimSpace(cfg.RefreshCookieSameSite)) { + case "lax", "strict", "none": + default: + return errors.New("refresh_cookie_same_site must be one of lax, strict, none") + } + if strings.EqualFold(strings.TrimSpace(cfg.RefreshCookieSameSite), "none") && !cfg.RefreshCookieSecure { + return errors.New("refresh_cookie_secure must be true when refresh_cookie_same_site is none") + } if isLockedEnvironment(cfg.Environment) { if cfg.JWTSecret == "dev-shared-secret" || len(cfg.JWTSecret) < 32 { return errors.New("jwt_secret must be a non-default value with at least 32 characters outside local/dev") diff --git a/server/admin/internal/modules/auth/handler.go b/server/admin/internal/modules/auth/handler.go index c6d20fd3..b4657c31 100644 --- a/server/admin/internal/modules/auth/handler.go +++ b/server/admin/internal/modules/auth/handler.go @@ -2,6 +2,7 @@ package auth import ( "net/http" + "strings" "github.com/gin-gonic/gin" "hyapp-admin-server/internal/config" @@ -112,6 +113,17 @@ func (h *Handler) ChangePassword(c *gin.Context) { } func (h *Handler) setRefreshCookie(c *gin.Context, token string, maxAge int) { - c.SetSameSite(http.SameSiteLaxMode) + c.SetSameSite(refreshCookieSameSite(h.cfg.RefreshCookieSameSite)) c.SetCookie(refreshCookieName, token, maxAge, "/api/v1/auth", "", h.cfg.RefreshCookieSecure, true) } + +func refreshCookieSameSite(value string) http.SameSite { + switch strings.ToLower(strings.TrimSpace(value)) { + case "strict": + return http.SameSiteStrictMode + case "none": + return http.SameSiteNoneMode + default: + return http.SameSiteLaxMode + } +}