fix: configure admin refresh cookie same site

This commit is contained in:
zhx 2026-06-04 15:32:43 +08:00
parent 86ae6db83f
commit 52b45d9b3d
4 changed files with 56 additions and 28 deletions

View File

@ -25,6 +25,7 @@ cors_allowed_origins:
- "http://hyapp-platform.global-interaction.com" - "http://hyapp-platform.global-interaction.com"
- "https://hyapp-platform.global-interaction.com" - "https://hyapp-platform.global-interaction.com"
refresh_cookie_secure: true refresh_cookie_secure: true
refresh_cookie_same_site: "none"
bootstrap: bootstrap:
# 仅首次初始化或显式 -bootstrap 时打开;生产常驻进程不要每次启动重放 seed。 # 仅首次初始化或显式 -bootstrap 时打开;生产常驻进程不要每次启动重放 seed。
enabled: false enabled: false

View File

@ -26,6 +26,7 @@ cors_allowed_origins:
- "http://localhost:7002" - "http://localhost:7002"
- "http://127.0.0.1:7002" - "http://127.0.0.1:7002"
refresh_cookie_secure: false refresh_cookie_secure: false
refresh_cookie_same_site: "lax"
bootstrap: bootstrap:
enabled: true enabled: true
seed_demo_data: true seed_demo_data: true

View File

@ -13,32 +13,33 @@ import (
) )
type Config struct { type Config struct {
ServiceName string `yaml:"service_name"` ServiceName string `yaml:"service_name"`
NodeID string `yaml:"node_id"` NodeID string `yaml:"node_id"`
Environment string `yaml:"environment"` Environment string `yaml:"environment"`
Log logging.Config `yaml:"log"` Log logging.Config `yaml:"log"`
HTTPAddr string `yaml:"http_addr"` HTTPAddr string `yaml:"http_addr"`
MySQLDSN string `yaml:"mysql_dsn"` MySQLDSN string `yaml:"mysql_dsn"`
UserMySQLDSN string `yaml:"user_mysql_dsn"` UserMySQLDSN string `yaml:"user_mysql_dsn"`
WalletMySQLDSN string `yaml:"wallet_mysql_dsn"` WalletMySQLDSN string `yaml:"wallet_mysql_dsn"`
MySQLAutoMigrate bool `yaml:"mysql_auto_migrate"` MySQLAutoMigrate bool `yaml:"mysql_auto_migrate"`
Migrations MigrationConfig `yaml:"migrations"` Migrations MigrationConfig `yaml:"migrations"`
JWTSecret string `yaml:"jwt_secret"` JWTSecret string `yaml:"jwt_secret"`
AccessTokenTTL time.Duration `yaml:"access_token_ttl"` AccessTokenTTL time.Duration `yaml:"access_token_ttl"`
RefreshTokenTTL time.Duration `yaml:"refresh_token_ttl"` RefreshTokenTTL time.Duration `yaml:"refresh_token_ttl"`
CORSAllowedOrigins []string `yaml:"cors_allowed_origins"` CORSAllowedOrigins []string `yaml:"cors_allowed_origins"`
RefreshCookieSecure bool `yaml:"refresh_cookie_secure"` RefreshCookieSecure bool `yaml:"refresh_cookie_secure"`
Bootstrap BootstrapConfig `yaml:"bootstrap"` RefreshCookieSameSite string `yaml:"refresh_cookie_same_site"`
BootstrapPassword string `yaml:"bootstrap_password"` Bootstrap BootstrapConfig `yaml:"bootstrap"`
UserService UserServiceConfig `yaml:"user_service"` BootstrapPassword string `yaml:"bootstrap_password"`
TencentCOS TencentCOSConfig `yaml:"tencent-cos"` UserService UserServiceConfig `yaml:"user_service"`
Redis RedisConfig `yaml:"redis"` TencentCOS TencentCOSConfig `yaml:"tencent-cos"`
Jobs JobsConfig `yaml:"jobs"` Redis RedisConfig `yaml:"redis"`
WalletService WalletServiceConfig `yaml:"wallet_service"` Jobs JobsConfig `yaml:"jobs"`
RoomService RoomServiceConfig `yaml:"room_service"` WalletService WalletServiceConfig `yaml:"wallet_service"`
ActivityService ActivityServiceConfig `yaml:"activity_service"` RoomService RoomServiceConfig `yaml:"room_service"`
GameService GameServiceConfig `yaml:"game_service"` ActivityService ActivityServiceConfig `yaml:"activity_service"`
StatisticsService StatisticsServiceConfig `yaml:"statistics_service"` GameService GameServiceConfig `yaml:"game_service"`
StatisticsService StatisticsServiceConfig `yaml:"statistics_service"`
} }
type MigrationConfig struct { type MigrationConfig struct {
@ -135,7 +136,8 @@ func Default() Config {
"http://localhost:7001", "http://localhost:7001",
"http://127.0.0.1:7001", "http://127.0.0.1:7001",
}, },
RefreshCookieSecure: false, RefreshCookieSecure: false,
RefreshCookieSameSite: "lax",
Bootstrap: BootstrapConfig{ Bootstrap: BootstrapConfig{
Enabled: true, Enabled: true,
SeedDemoData: true, SeedDemoData: true,
@ -225,6 +227,10 @@ func (cfg *Config) Normalize() {
if cfg.Log.MaxPayloadBytes <= 0 { if cfg.Log.MaxPayloadBytes <= 0 {
cfg.Log.MaxPayloadBytes = 2048 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.ServiceName = strings.TrimSpace(cfg.ServiceName)
cfg.NodeID = strings.TrimSpace(cfg.NodeID) cfg.NodeID = strings.TrimSpace(cfg.NodeID)
if cfg.NodeID == "" { if cfg.NodeID == "" {
@ -332,6 +338,14 @@ func (cfg Config) Validate() error {
if cfg.RefreshTokenTTL <= 0 { if cfg.RefreshTokenTTL <= 0 {
return errors.New("refresh_token_ttl must be greater than 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 isLockedEnvironment(cfg.Environment) {
if cfg.JWTSecret == "dev-shared-secret" || len(cfg.JWTSecret) < 32 { 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") return errors.New("jwt_secret must be a non-default value with at least 32 characters outside local/dev")

View File

@ -2,6 +2,7 @@ package auth
import ( import (
"net/http" "net/http"
"strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"hyapp-admin-server/internal/config" "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) { 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) 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
}
}