Make auth cookie SameSite configurable

Add same-site to auth cookie config with lax as the default,
PROBOD_AUTH_COOKIE_SAMESITE bootstrap mapping, and validation
that rejects none unless Secure is enabled.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
This commit is contained in:
Cursor Agent
2026-07-29 16:37:28 +00:00
committed by Bryan Frimin
parent cd6c46212a
commit 62d0ab68c4
9 changed files with 266 additions and 26 deletions

View File

@@ -59,6 +59,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
pgCACertBundle := b.getPgCACertBundle()
authCookieSameSite, err := probodconfig.ParseCookieSameSite(
b.resolver.getEnvOrDefault("PROBOD_AUTH_COOKIE_SAMESITE", "lax"),
)
if err != nil {
return nil, fmt.Errorf("cannot parse PROBOD_AUTH_COOKIE_SAMESITE: %w", err)
}
cfg := &probodconfig.FullConfig{
Unit: probodconfig.UnitConfig{
Metrics: probodconfig.MetricsConfig{
@@ -118,6 +125,7 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
Secret: b.resolver.getEnv("PROBOD_AUTH_COOKIE_SECRET"),
Duration: b.resolver.getEnvIntOrDefault("PROBOD_AUTH_COOKIE_DURATION", 24),
Secure: b.resolver.getEnvBoolOrDefault("PROBOD_AUTH_COOKIE_SECURE", true),
SameSite: authCookieSameSite,
},
Password: probodconfig.PasswordConfig{
Pepper: b.resolver.getEnv("PROBOD_AUTH_PASSWORD_PEPPER"),
@@ -559,6 +567,10 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
return nil, b.resolver.Err()
}
if err := cfg.Probod.Auth.Cookie.Validate(); err != nil {
return nil, fmt.Errorf("cannot validate auth cookie config: %w", err)
}
return cfg, nil
}

View File

@@ -335,6 +335,7 @@ func TestBuilder_Build_CustomValues(t *testing.T) {
env["PROBOD_AUTH_EMAIL_CONFIRMATION_TOKEN_VALIDITY"] = "43200"
env["PROBOD_AUTH_COOKIE_DOMAIN"] = ".example.com"
env["PROBOD_AUTH_COOKIE_DURATION"] = "48"
env["PROBOD_AUTH_COOKIE_SAMESITE"] = "strict"
// SAML
env["PROBOD_SAML_DOMAIN_VERIFICATION_INTERVAL_SECONDS"] = "120"
env["PROBOD_SAML_DOMAIN_VERIFICATION_RESOLVER_ADDR"] = "1.1.1.1:53"
@@ -471,6 +472,7 @@ func TestBuilder_Build_CustomValues(t *testing.T) {
assert.Equal(t, 43200, cfg.Probod.Auth.EmailConfirmationTokenValidity)
assert.Equal(t, ".example.com", cfg.Probod.Auth.Cookie.Domain)
assert.Equal(t, 48, cfg.Probod.Auth.Cookie.Duration)
assert.Equal(t, probodconfig.CookieSameSiteStrict, cfg.Probod.Auth.Cookie.SameSite)
// SAML
assert.Equal(t, 120, cfg.Probod.Auth.SAML.DomainVerificationIntervalSeconds)
assert.Equal(t, "1.1.1.1:53", cfg.Probod.Auth.SAML.DomainVerificationResolverAddr)
@@ -896,6 +898,33 @@ func TestBuilder_Build_PgCABundleFromFile(t *testing.T) {
assert.Equal(t, "ca-bundle-from-file", cfg.Probod.Pg.CACertBundle)
}
func TestBuilder_Build_AuthCookieSameSiteInvalid(t *testing.T) {
env := requiredEnv()
env["PROBOD_AUTH_COOKIE_SAMESITE"] = "invalid"
b := NewBuilder(NewResolver(mockEnv(env)))
b.samlCertificate = "test-cert"
b.samlPrivateKey = "test-key"
_, err := b.Build()
require.Error(t, err)
assert.Contains(t, err.Error(), "PROBOD_AUTH_COOKIE_SAMESITE")
}
func TestBuilder_Build_AuthCookieSameSiteNoneRequiresSecure(t *testing.T) {
env := requiredEnv()
env["PROBOD_AUTH_COOKIE_SAMESITE"] = "none"
env["PROBOD_AUTH_COOKIE_SECURE"] = "false"
b := NewBuilder(NewResolver(mockEnv(env)))
b.samlCertificate = "test-cert"
b.samlPrivateKey = "test-key"
_, err := b.Build()
require.Error(t, err)
assert.Contains(t, err.Error(), "secure")
}
func TestBuilder_parseOriginsList(t *testing.T) {
tests := []struct {
name string