feat: support for session options in alpha config and refactoring of cookie options

Signed-off-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
Jan Larwig
2026-03-26 22:24:21 +01:00
parent dcef6117cb
commit 64bf0fc6f3
32 changed files with 675 additions and 428 deletions
+47 -30
View File
@@ -2,49 +2,56 @@ package options
import (
"fmt"
"os"
"time"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util/ptr"
"go.yaml.in/yaml/v3"
)
const (
// DefaultCookieSecure is the default value for Cookie.Secure
DefaultCookieSecure bool = true
// DefaultCookieHTTPOnly is the default value for Cookie.HTTPOnly
DefaultCookieHTTPOnly bool = true
// DefaultCookieInsecure is the default value for Cookie.Insecure
DefaultCookieInsecure bool = false
// DefaultCookieNotHttpOnly is the default value for Cookie.NotHttpOnly
DefaultCookieNotHttpOnly bool = false
// DefaultCSRFPerRequest is the default value for Cookie.CSRFPerRequest
DefaultCSRFPerRequest bool = false
)
type SameSiteMode string
const (
SameSiteLax SameSiteMode = "lax"
SameSiteStrict SameSiteMode = "strict"
SameSiteNone SameSiteMode = "none"
SameSiteDefault SameSiteMode = ""
)
// Cookie contains configuration options relating session and CSRF cookies
type Cookie struct {
// Name is the name of the cookie
Name string `yaml:"name,omitempty"`
// Secret is the secret used to encrypt/sign the cookie value
Secret string `yaml:"secret,omitempty"`
// SecretFile is a file containing the secret used to encrypt/sign the cookie value
// instead of specifying it directly in the config. Secret takes precedence over SecretFile
SecretFile string `yaml:"secretFile,omitempty"`
// Secret is the secret source used to encrypt/sign the cookie value
Secret SecretSource `yaml:"secret,omitempty"`
// Domains is a list of domains for which the cookie is valid
Domains []string `yaml:"domains,omitempty"`
// Path is the path for which the cookie is valid
Path string `yaml:"path,omitempty"`
// Expire is the duration before the cookie expires
Expire time.Duration `yaml:"expire,omitempty"`
// Refresh is the duration after which the cookie is refreshable
Refresh time.Duration `yaml:"refresh,omitempty"`
// Secure indicates whether the cookie is only sent over HTTPS
Secure *bool `yaml:"secure,omitempty"`
// HTTPOnly indicates whether the cookie is inaccessible to JavaScript
HTTPOnly *bool `yaml:"httpOnly,omitempty"`
// SameSite sets the SameSite attribute on the session cookies
SameSite string `yaml:"sameSite,omitempty"`
// Insecure indicates whether the cookie allows to be sent over HTTP
// Default is false, which requires HTTPS
Insecure *bool `yaml:"insecure,omitempty"`
// NotHttpOnly is the inverse of HTTPOnly; indicates whether the cookie is accessible to JavaScript
// Default is false, which helps mitigate certain XSS attacks
NotHttpOnly *bool `yaml:"notHttpOnly,omitempty"`
// SameSite sets the SameSite attribute on the cookie
SameSite SameSiteMode `yaml:"sameSite,omitempty"`
// CSRFSameSite sets the SameSite attribute on the csrf cookies
CSRFSameSite string `yaml:"sameSite,omitempty"`
CSRFSameSite SameSiteMode `yaml:"sameSite,omitempty"`
// CSRFPerRequest indicates whether a unique CSRF token is generated for each request
// Enables parallel requests from clients (e.g., multiple tabs)
// Default is false, which uses a single CSRF token per session
CSRFPerRequest *bool `yaml:"csrfPerRequest,omitempty"`
// CSRFPerRequestLimit sets a limit on the number of valid CSRF tokens when CSRFPerRequest is enabled
// Used to prevent unbounded memory growth from storing too many tokens
@@ -53,18 +60,28 @@ type Cookie struct {
CSRFExpire time.Duration `yaml:"csrfExpire,omitempty"`
}
// GetSecret returns the cookie secret, reading from file if SecretFile is set
func (c *Cookie) GetSecret() (secret string, err error) {
if c.Secret != "" || c.SecretFile == "" {
return c.Secret, nil
func (m *SameSiteMode) UnmarshalYAML(value *yaml.Node) error {
var s string
if err := value.Decode(&s); err != nil {
return err
}
switch SameSiteMode(s) {
case SameSiteLax, SameSiteStrict, SameSiteNone, SameSiteDefault:
*m = SameSiteMode(s)
return nil
default:
return fmt.Errorf("invalid same site mode: %s", s)
}
}
fileSecret, err := os.ReadFile(c.SecretFile)
// GetSecret returns the cookie secret as a string from the SecretSource
func (c *Cookie) GetSecret() (string, error) {
secret, err := c.Secret.GetSecretValue()
if err != nil {
return "", fmt.Errorf("error reading cookie secret file %s: %w", c.SecretFile, err)
return "", fmt.Errorf("error getting cookie secret: %w", err)
}
return string(fileSecret), nil
return string(secret), nil
}
// EnsureDefaults sets any default values for the Cookie configuration
@@ -78,11 +95,11 @@ func (c *Cookie) EnsureDefaults() {
if c.Expire == 0 {
c.Expire = time.Duration(168) * time.Hour
}
if c.Secure == nil {
c.Secure = ptr.To(DefaultCookieSecure)
if c.Insecure == nil {
c.Insecure = ptr.To(DefaultCookieInsecure)
}
if c.HTTPOnly == nil {
c.HTTPOnly = ptr.To(DefaultCookieHTTPOnly)
if c.NotHttpOnly == nil {
c.NotHttpOnly = ptr.To(DefaultCookieNotHttpOnly)
}
if c.CSRFPerRequest == nil {
c.CSRFPerRequest = ptr.To(DefaultCSRFPerRequest)
+20 -10
View File
@@ -10,8 +10,10 @@ import (
func TestCookieGetSecret(t *testing.T) {
t.Run("returns secret when Secret is set", func(t *testing.T) {
c := &Cookie{
Secret: "my-secret",
SecretFile: "",
Secret: SecretSource{
Value: []byte("my-secret"),
FromFile: "",
},
}
secret, err := c.GetSecret()
assert.NoError(t, err)
@@ -20,8 +22,10 @@ func TestCookieGetSecret(t *testing.T) {
t.Run("returns secret when both Secret and SecretFile are set", func(t *testing.T) {
c := &Cookie{
Secret: "my-secret",
SecretFile: "/some/file",
Secret: SecretSource{
Value: []byte("my-secret"),
FromFile: "/some/file",
},
}
secret, err := c.GetSecret()
assert.NoError(t, err)
@@ -39,8 +43,10 @@ func TestCookieGetSecret(t *testing.T) {
tmpfile.Close()
c := &Cookie{
Secret: "",
SecretFile: tmpfile.Name(),
Secret: SecretSource{
Value: []byte(""),
FromFile: tmpfile.Name(),
},
}
secret, err := c.GetSecret()
assert.NoError(t, err)
@@ -49,8 +55,10 @@ func TestCookieGetSecret(t *testing.T) {
t.Run("returns error when file does not exist", func(t *testing.T) {
c := &Cookie{
Secret: "",
SecretFile: "/nonexistent/file",
Secret: SecretSource{
Value: []byte(""),
FromFile: "/nonexistent/file",
},
}
secret, err := c.GetSecret()
assert.Error(t, err)
@@ -60,8 +68,10 @@ func TestCookieGetSecret(t *testing.T) {
t.Run("returns empty when both Secret and SecretFile are empty", func(t *testing.T) {
c := &Cookie{
Secret: "",
SecretFile: "",
Secret: SecretSource{
Value: []byte(""),
FromFile: "",
},
}
secret, err := c.GetSecret()
assert.NoError(t, err)
+19 -6
View File
@@ -39,21 +39,34 @@ func legacyCookieFlagSet() *pflag.FlagSet {
flagSet.Bool("cookie-csrf-per-request", false, "When this property is set to true, then the CSRF cookie name is built based on the state and varies per request. If property is set to false, then CSRF cookie has the same name for all requests.")
flagSet.Int("cookie-csrf-per-request-limit", 0, "Sets a limit on the number of CSRF requests cookies that oauth2-proxy will create. The oldest cookies will be removed. Useful if users end up with 431 Request headers too large status codes.")
flagSet.Duration("cookie-csrf-expire", time.Duration(15)*time.Minute, "expire timeframe for CSRF cookie")
return flagSet
}
func (l *LegacyCookie) convert() Cookie {
// Invert Secure and HTTPOnly to match the new Cookie struct
// which uses Insecure and NotHttpOnly
insecure := !l.Secure
notHTTPOnly := !l.HTTPOnly
var secret *SecretSource
if l.Secret != "" {
secret = NewSecretSourceFromString(l.Secret)
} else if l.SecretFile != "" {
secret = &SecretSource{
FromFile: l.SecretFile,
}
}
return Cookie{
Name: l.Name,
Secret: l.Secret,
SecretFile: l.SecretFile,
Secret: *secret,
Domains: l.Domains,
Path: l.Path,
Expire: l.Expire,
Refresh: l.Refresh,
Secure: &l.Secure,
HTTPOnly: &l.HTTPOnly,
SameSite: l.SameSite,
Insecure: &insecure,
NotHttpOnly: &notHTTPOnly,
SameSite: SameSiteMode(l.SameSite),
CSRFPerRequest: &l.CSRFPerRequest,
CSRFPerRequestLimit: l.CSRFPerRequestLimit,
CSRFExpire: l.CSRFExpire,
+3 -5
View File
@@ -106,11 +106,9 @@ func getBasicAuthHeader(preferEmailToUser bool, basicAuthPassword string) Header
Values: []HeaderValue{
{
ClaimSource: &ClaimSource{
Claim: claim,
Prefix: "Basic ",
BasicAuthPassword: &SecretSource{
Value: []byte(basicAuthPassword),
},
Claim: claim,
Prefix: "Basic ",
BasicAuthPassword: NewSecretSourceFromString(basicAuthPassword),
},
},
},
+12
View File
@@ -23,6 +23,9 @@ type LegacyOptions struct {
// Legacy options for cookie configuration
LegacyCookie LegacyCookie `cfg:",squash"`
// Legacy options for session store configuration
LegacySessionOptions LegacySessionOptions `cfg:",squash"`
Options Options `cfg:",squash"`
}
@@ -75,6 +78,13 @@ func NewLegacyOptions() *LegacyOptions {
CSRFExpire: time.Duration(15) * time.Minute,
},
LegacySessionOptions: LegacySessionOptions{
Type: "cookie",
Cookie: LegacyCookieStoreOptions{
Minimal: false,
},
},
Options: *NewOptions(),
}
}
@@ -88,6 +98,7 @@ func NewLegacyFlagSet() *pflag.FlagSet {
flagSet.AddFlagSet(legacyProviderFlagSet())
flagSet.AddFlagSet(legacyGoogleFlagSet())
flagSet.AddFlagSet(legacyCookieFlagSet())
flagSet.AddFlagSet(legacySessionFlagSet())
return flagSet
}
@@ -110,6 +121,7 @@ func (l *LegacyOptions) ToOptions() (*Options, error) {
}
l.Options.Providers = providers
l.Options.Cookie = l.LegacyCookie.convert()
l.Options.Session = l.LegacySessionOptions.convert(l.LegacyCookie.Refresh)
l.Options.EnsureDefaults()
+3 -4
View File
@@ -1096,13 +1096,12 @@ var _ = Describe("Legacy Options", func() {
// Test cases and expected outcomes
fullCookie := Cookie{
Name: "_oauth2_proxy",
Secret: "",
Secret: SecretSource{},
Domains: nil,
Path: "/",
Expire: time.Duration(168) * time.Hour,
Refresh: time.Duration(0),
Secure: ptr.To(true),
HTTPOnly: ptr.To(true),
Insecure: ptr.To(false),
NotHttpOnly: ptr.To(false),
SameSite: "",
CSRFPerRequest: ptr.To(false),
CSRFPerRequestLimit: 0,
+79
View File
@@ -0,0 +1,79 @@
package options
import (
"time"
"github.com/spf13/pflag"
)
// LegacySessionOptions contains configuration options for the SessionStore providers.
type LegacySessionOptions struct {
Type string `flag:"session-store-type" cfg:"session_store_type"`
Cookie LegacyCookieStoreOptions `cfg:",squash"`
Redis LegacyRedisStoreOptions `cfg:",squash"`
}
// LegacyCookieStoreOptions contains configuration options for the CookieSessionStore.
type LegacyCookieStoreOptions struct {
Minimal bool `flag:"session-cookie-minimal" cfg:"session_cookie_minimal"`
}
// RedisStoreOptions contains configuration options for the RedisSessionStore.
type LegacyRedisStoreOptions struct {
ConnectionURL string `flag:"redis-connection-url" cfg:"redis_connection_url"`
Username string `flag:"redis-username" cfg:"redis_username"`
Password string `flag:"redis-password" cfg:"redis_password"`
UseSentinel bool `flag:"redis-use-sentinel" cfg:"redis_use_sentinel"`
SentinelPassword string `flag:"redis-sentinel-password" cfg:"redis_sentinel_password"`
SentinelMasterName string `flag:"redis-sentinel-master-name" cfg:"redis_sentinel_master_name"`
SentinelConnectionURLs []string `flag:"redis-sentinel-connection-urls" cfg:"redis_sentinel_connection_urls"`
UseCluster bool `flag:"redis-use-cluster" cfg:"redis_use_cluster"`
ClusterConnectionURLs []string `flag:"redis-cluster-connection-urls" cfg:"redis_cluster_connection_urls"`
CAPath string `flag:"redis-ca-path" cfg:"redis_ca_path"`
InsecureSkipTLSVerify bool `flag:"redis-insecure-skip-tls-verify" cfg:"redis_insecure_skip_tls_verify"`
IdleTimeout int `flag:"redis-connection-idle-timeout" cfg:"redis_connection_idle_timeout"`
}
func legacySessionFlagSet() *pflag.FlagSet {
flagSet := pflag.NewFlagSet("session", pflag.ExitOnError)
flagSet.String("session-store-type", "cookie", "the session storage provider to use")
flagSet.Bool("session-cookie-minimal", false, "strip OAuth tokens from cookie session stores if they aren't needed (cookie session store only)")
flagSet.String("redis-connection-url", "", "URL of redis server for redis session storage (eg: redis://[USER[:PASSWORD]@]HOST[:PORT])")
flagSet.String("redis-username", "", "Redis username. Applicable for Redis configurations where ACL has been configured. Will override any username set in `--redis-connection-url`")
flagSet.String("redis-password", "", "Redis password. Applicable for all Redis configurations. Will override any password set in `--redis-connection-url`")
flagSet.Bool("redis-use-sentinel", false, "Connect to redis via sentinels. Must set --redis-sentinel-master-name and --redis-sentinel-connection-urls to use this feature")
flagSet.String("redis-sentinel-password", "", "Redis sentinel password. Used only for sentinel connection; any redis node passwords need to use `--redis-password`")
flagSet.String("redis-sentinel-master-name", "", "Redis sentinel master name. Used in conjunction with --redis-use-sentinel")
flagSet.String("redis-ca-path", "", "Redis custom CA path")
flagSet.Bool("redis-insecure-skip-tls-verify", false, "Use insecure TLS connection to redis")
flagSet.StringSlice("redis-sentinel-connection-urls", []string{}, "List of Redis sentinel connection URLs (eg redis://[USER[:PASSWORD]@]HOST[:PORT]). Used in conjunction with --redis-use-sentinel")
flagSet.Bool("redis-use-cluster", false, "Connect to redis cluster. Must set --redis-cluster-connection-urls to use this feature")
flagSet.StringSlice("redis-cluster-connection-urls", []string{}, "List of Redis cluster connection URLs (eg redis://[USER[:PASSWORD]@]HOST[:PORT]). Used in conjunction with --redis-use-cluster")
flagSet.Int("redis-connection-idle-timeout", 0, "Redis connection idle timeout seconds, if Redis timeout option is non-zero, the --redis-connection-idle-timeout must be less then Redis timeout option")
return flagSet
}
func (l *LegacySessionOptions) convert(legacyCookieRefresh time.Duration) SessionOptions {
return SessionOptions{
Type: SessionStoreType(l.Type),
Refresh: legacyCookieRefresh,
Cookie: CookieStoreOptions{
Minimal: &l.Cookie.Minimal,
},
Redis: RedisStoreOptions{
ConnectionURL: l.Redis.ConnectionURL,
Password: l.Redis.Password,
UseSentinel: &l.Redis.UseSentinel,
SentinelPassword: l.Redis.SentinelPassword,
SentinelMasterName: l.Redis.SentinelMasterName,
SentinelConnectionURLs: l.Redis.SentinelConnectionURLs,
UseCluster: &l.Redis.UseCluster,
ClusterConnectionURLs: l.Redis.ClusterConnectionURLs,
CAPath: l.Redis.CAPath,
InsecureSkipTLSVerify: &l.Redis.InsecureSkipTLSVerify,
IdleTimeout: l.Redis.IdleTimeout,
},
}
}
+7 -1
View File
@@ -60,6 +60,13 @@ var _ = Describe("Load", func() {
CSRFExpire: time.Duration(15) * time.Minute,
},
LegacySessionOptions: LegacySessionOptions{
Type: "cookie",
Cookie: LegacyCookieStoreOptions{
Minimal: false,
},
},
Options: Options{
BearerTokenLoginFallback: true,
ProxyPrefix: "/oauth2",
@@ -67,7 +74,6 @@ var _ = Describe("Load", func() {
ReadyPath: "/ready",
RealClientIPHeader: "X-Real-IP",
ForceHTTPS: false,
Session: sessionOptionsDefaults(),
Templates: templatesDefaults(),
SkipAuthPreflight: false,
Logging: loggingDefaults(),
+1 -17
View File
@@ -105,7 +105,6 @@ func NewOptions() *Options {
ReadyPath: "/ready",
RealClientIPHeader: "X-Real-IP",
ForceHTTPS: false,
Session: sessionOptionsDefaults(),
Templates: templatesDefaults(),
SkipAuthPreflight: false,
Logging: loggingDefaults(),
@@ -144,20 +143,6 @@ func NewFlagSet() *pflag.FlagSet {
flagSet.String("ping-path", "/ping", "the ping endpoint that can be used for basic health checks")
flagSet.String("ping-user-agent", "", "special User-Agent that will be used for basic health checks")
flagSet.String("ready-path", "/ready", "the ready endpoint that can be used for deep health checks")
flagSet.String("session-store-type", "cookie", "the session storage provider to use")
flagSet.Bool("session-cookie-minimal", false, "strip OAuth tokens from cookie session stores if they aren't needed (cookie session store only)")
flagSet.String("redis-connection-url", "", "URL of redis server for redis session storage (eg: redis://[USER[:PASSWORD]@]HOST[:PORT])")
flagSet.String("redis-username", "", "Redis username. Applicable for Redis configurations where ACL has been configured. Will override any username set in `--redis-connection-url`")
flagSet.String("redis-password", "", "Redis password. Applicable for all Redis configurations. Will override any password set in `--redis-connection-url`")
flagSet.Bool("redis-use-sentinel", false, "Connect to redis via sentinels. Must set --redis-sentinel-master-name and --redis-sentinel-connection-urls to use this feature")
flagSet.String("redis-sentinel-password", "", "Redis sentinel password. Used only for sentinel connection; any redis node passwords need to use `--redis-password`")
flagSet.String("redis-sentinel-master-name", "", "Redis sentinel master name. Used in conjunction with --redis-use-sentinel")
flagSet.String("redis-ca-path", "", "Redis custom CA path")
flagSet.Bool("redis-insecure-skip-tls-verify", false, "Use insecure TLS connection to redis")
flagSet.StringSlice("redis-sentinel-connection-urls", []string{}, "List of Redis sentinel connection URLs (eg redis://[USER[:PASSWORD]@]HOST[:PORT]). Used in conjunction with --redis-use-sentinel")
flagSet.Bool("redis-use-cluster", false, "Connect to redis cluster. Must set --redis-cluster-connection-urls to use this feature")
flagSet.StringSlice("redis-cluster-connection-urls", []string{}, "List of Redis cluster connection URLs (eg redis://[USER[:PASSWORD]@]HOST[:PORT]). Used in conjunction with --redis-use-cluster")
flagSet.Int("redis-connection-idle-timeout", 0, "Redis connection idle timeout seconds, if Redis timeout option is non-zero, the --redis-connection-idle-timeout must be less then Redis timeout option")
flagSet.String("signature-key", "", "GAP-Signature request signature key (algorithm:secretkey)")
flagSet.Bool("gcp-healthchecks", false, "Enable GCP/GKE healthcheck endpoints")
@@ -181,9 +166,8 @@ func (o *Options) EnsureDefaults() {
}
o.Cookie.EnsureDefaults()
o.Session.EnsureDefaults()
// TBD: Uncomment as we add EnsureDefaults methods
// o.Session.EnsureDefaults()
// o.Templates.EnsureDefaults()
// o.Logging.EnsureDefaults()
}
+43
View File
@@ -1,5 +1,11 @@
package options
import (
"encoding/base64"
"fmt"
"os"
)
// SecretSource references an individual secret value.
// Only one source within the struct should be defined at any time.
type SecretSource struct {
@@ -13,6 +19,43 @@ type SecretSource struct {
FromFile string `yaml:"fromFile,omitempty"`
}
func NewSecretSourceFromValue(value []byte) *SecretSource {
encoded := make([]byte, base64.RawStdEncoding.EncodedLen(len(value)))
base64.RawStdEncoding.Encode(encoded, value)
return &SecretSource{
Value: encoded,
}
}
func NewSecretSourceFromString(s string) *SecretSource {
return NewSecretSourceFromValue([]byte(s))
}
func (ss *SecretSource) GetSecretValue() ([]byte, error) {
if len(ss.Value) > 0 {
var decoded []byte
if _, err := base64.RawStdEncoding.Decode(decoded, ss.Value); err != nil {
return nil, fmt.Errorf("error decoding secret value: %w", err)
}
return decoded, nil
}
if ss.FromEnv != "" {
envValue := os.Getenv(ss.FromEnv)
return []byte(envValue), nil
}
if ss.FromFile != "" {
fileData, err := os.ReadFile(ss.FromFile)
if err != nil {
return nil, fmt.Errorf("error reading secret from file %q: %w", ss.FromFile, err)
}
return fileData, nil
}
return nil, nil
}
// EnsureDefaults sets any default values for SecretSource fields.
func (ss *SecretSource) EnsureDefaults() {
// No defaults to set currently
+85 -30
View File
@@ -1,46 +1,101 @@
package options
import (
"time"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util/ptr"
)
type SessionStoreType string
const (
// CookieSessionStoreType is used to indicate the CookieSessionStore should be
// used for storing sessions.
CookieSessionStoreType SessionStoreType = "cookie"
// RedisSessionStoreType is used to indicate the RedisSessionStore should be
// used for storing sessions.
RedisSessionStoreType SessionStoreType = "redis"
// DefaultCookieStoreMinimal is the default value for CookieStoreOptions.Minimal
DefaultCookieStoreMinimal bool = false
// DefaultRedisStoreUseSentinel is the default value for RedisStoreOptions.UseSentinel
DefaultRedisStoreUseSentinel bool = false
// DefaultRedisStoreUseCluster is the default value for RedisStoreOptions.UseCluster
DefaultRedisStoreUseCluster bool = false
// DefaultRedisStoreInsecureSkipTLSVerify is the default value for RedisStoreOptions.InsecureSkipTLSVerify
DefaultRedisStoreInsecureSkipTLSVerify bool = false
)
// SessionOptions contains configuration options for the SessionStore providers.
type SessionOptions struct {
Type string `flag:"session-store-type" cfg:"session_store_type"`
Cookie CookieStoreOptions `cfg:",squash"`
Redis RedisStoreOptions `cfg:",squash"`
// Type is the type of session store to use
// Options are "cookie" or "redis"
// Default is "cookie"
Type SessionStoreType `yaml:"type,omitempty"`
// Refresh is the duration after which the session is refreshable
Refresh time.Duration `yaml:"refresh,omitempty"`
// Cookie is the configuration options for the CookieSessionStore
Cookie CookieStoreOptions `yaml:"cookie,omitempty"`
// Redis is the configuration options for the RedisSessionStore
Redis RedisStoreOptions `yaml:"redis,omitempty"`
}
// CookieSessionStoreType is used to indicate the CookieSessionStore should be
// used for storing sessions.
var CookieSessionStoreType = "cookie"
// RedisSessionStoreType is used to indicate the RedisSessionStore should be
// used for storing sessions.
var RedisSessionStoreType = "redis"
// CookieStoreOptions contains configuration options for the CookieSessionStore.
type CookieStoreOptions struct {
Minimal bool `flag:"session-cookie-minimal" cfg:"session_cookie_minimal"`
// Minimal indicates whether to use minimal cookies for session storage
// Default is false
Minimal *bool `yaml:"minimal,omitempty"`
}
// RedisStoreOptions contains configuration options for the RedisSessionStore.
type RedisStoreOptions struct {
ConnectionURL string `flag:"redis-connection-url" cfg:"redis_connection_url"`
Username string `flag:"redis-username" cfg:"redis_username"`
Password string `flag:"redis-password" cfg:"redis_password"`
UseSentinel bool `flag:"redis-use-sentinel" cfg:"redis_use_sentinel"`
SentinelPassword string `flag:"redis-sentinel-password" cfg:"redis_sentinel_password"`
SentinelMasterName string `flag:"redis-sentinel-master-name" cfg:"redis_sentinel_master_name"`
SentinelConnectionURLs []string `flag:"redis-sentinel-connection-urls" cfg:"redis_sentinel_connection_urls"`
UseCluster bool `flag:"redis-use-cluster" cfg:"redis_use_cluster"`
ClusterConnectionURLs []string `flag:"redis-cluster-connection-urls" cfg:"redis_cluster_connection_urls"`
CAPath string `flag:"redis-ca-path" cfg:"redis_ca_path"`
InsecureSkipTLSVerify bool `flag:"redis-insecure-skip-tls-verify" cfg:"redis_insecure_skip_tls_verify"`
IdleTimeout int `flag:"redis-connection-idle-timeout" cfg:"redis_connection_idle_timeout"`
// ConnectionURL is the Redis connection URL
ConnectionURL string `yaml:"connectionURL,omitempty"`
// Username is the Redis username
Username string `yaml:"username,omitempty"`
// Password is the Redis password
Password string `yaml:"password,omitempty"`
// UseSentinel indicates whether to use Redis Sentinel
// Default is false
UseSentinel *bool `yaml:"useSentinel,omitempty"`
// SentinelPassword is the Redis Sentinel password
SentinelPassword string `yaml:"sentinelPassword,omitempty"`
// SentinelMasterName is the Redis Sentinel master name
SentinelMasterName string `yaml:"sentinelMasterName,omitempty"`
// SentinelConnectionURLs is a list of Redis Sentinel connection URLs
SentinelConnectionURLs []string `yaml:"sentinelConnectionURLs,omitempty"`
// UseCluster indicates whether to use Redis Cluster
// Default is false
UseCluster *bool `yaml:"useCluster,omitempty"`
// ClusterConnectionURLs is a list of Redis Cluster connection URLs
ClusterConnectionURLs []string `yaml:"clusterConnectionURLs,omitempty"`
// CAPath is the path to the CA certificate for Redis TLS connections
CAPath string `yaml:"caPath,omitempty"`
// InsecureSkipTLSVerify indicates whether to skip TLS verification for Redis connections
InsecureSkipTLSVerify *bool `yaml:"insecureSkipTLSVerify,omitempty"`
// IdleTimeout is the Redis connection idle timeout in seconds
IdleTimeout int `yaml:"idleTimeout,omitempty"`
}
func sessionOptionsDefaults() SessionOptions {
return SessionOptions{
Type: CookieSessionStoreType,
Cookie: CookieStoreOptions{
Minimal: false,
},
// EnsureDefaults sets default values for SessionOptions
func (s *SessionOptions) EnsureDefaults() {
if s.Type == "" {
s.Type = CookieSessionStoreType
}
if s.Cookie.Minimal == nil {
s.Cookie.Minimal = ptr.To(DefaultCookieStoreMinimal)
}
if s.Redis.UseSentinel == nil {
s.Redis.UseSentinel = ptr.To(DefaultRedisStoreUseSentinel)
}
if s.Redis.UseCluster == nil {
s.Redis.UseCluster = ptr.To(DefaultRedisStoreUseCluster)
}
if s.Redis.InsecureSkipTLSVerify == nil {
s.Redis.InsecureSkipTLSVerify = ptr.To(DefaultRedisStoreInsecureSkipTLSVerify)
}
}