Skip to content

Commit 22a4ab3

Browse files
author
Hein
committed
feat(security): add session cookie management functions
* Introduce SessionCookieOptions for configurable session cookies * Implement SetSessionCookie, GetSessionCookie, and ClearSessionCookie functions * Enhance cookie handling in DatabaseAuthenticator
1 parent e289c2e commit 22a4ab3

File tree

2 files changed

+121
-3
lines changed

2 files changed

+121
-3
lines changed

pkg/security/middleware.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,125 @@ func GetUserMeta(ctx context.Context) (map[string]any, bool) {
456456
return meta, ok
457457
}
458458

459+
// SessionCookieOptions configures the session cookie set by SetSessionCookie.
460+
// All fields are optional; sensible secure defaults are applied when omitted.
461+
type SessionCookieOptions struct {
462+
// Name is the cookie name. Defaults to "session_token".
463+
Name string
464+
// Path is the cookie path. Defaults to "/".
465+
Path string
466+
// Domain restricts the cookie to a specific domain. Empty means current host.
467+
Domain string
468+
// Secure sets the Secure flag. Defaults to true.
469+
// Set to false only in local development over HTTP.
470+
Secure *bool
471+
// SameSite sets the SameSite policy. Defaults to http.SameSiteLaxMode.
472+
SameSite http.SameSite
473+
}
474+
475+
func (o SessionCookieOptions) name() string {
476+
if o.Name != "" {
477+
return o.Name
478+
}
479+
return "session_token"
480+
}
481+
482+
func (o SessionCookieOptions) path() string {
483+
if o.Path != "" {
484+
return o.Path
485+
}
486+
return "/"
487+
}
488+
489+
func (o SessionCookieOptions) secure() bool {
490+
if o.Secure != nil {
491+
return *o.Secure
492+
}
493+
return true
494+
}
495+
496+
func (o SessionCookieOptions) sameSite() http.SameSite {
497+
if o.SameSite != 0 {
498+
return o.SameSite
499+
}
500+
return http.SameSiteLaxMode
501+
}
502+
503+
// SetSessionCookie writes the session_token cookie to the response after a successful login.
504+
// Call this immediately after a successful Authenticator.Login() call.
505+
//
506+
// Example:
507+
//
508+
// resp, err := auth.Login(r.Context(), req)
509+
// if err != nil { ... }
510+
// security.SetSessionCookie(w, resp)
511+
// json.NewEncoder(w).Encode(resp)
512+
func SetSessionCookie(w http.ResponseWriter, loginResp *LoginResponse, opts ...SessionCookieOptions) {
513+
var o SessionCookieOptions
514+
if len(opts) > 0 {
515+
o = opts[0]
516+
}
517+
518+
maxAge := 0
519+
if loginResp.ExpiresIn > 0 {
520+
maxAge = int(loginResp.ExpiresIn)
521+
}
522+
523+
http.SetCookie(w, &http.Cookie{
524+
Name: o.name(),
525+
Value: loginResp.Token,
526+
Path: o.path(),
527+
Domain: o.Domain,
528+
MaxAge: maxAge,
529+
HttpOnly: true,
530+
Secure: o.secure(),
531+
SameSite: o.sameSite(),
532+
})
533+
}
534+
535+
// GetSessionCookie returns the session token value from the request cookie, or empty string if not present.
536+
//
537+
// Example:
538+
//
539+
// token := security.GetSessionCookie(r)
540+
func GetSessionCookie(r *http.Request, opts ...SessionCookieOptions) string {
541+
var o SessionCookieOptions
542+
if len(opts) > 0 {
543+
o = opts[0]
544+
}
545+
cookie, err := r.Cookie(o.name())
546+
if err != nil {
547+
return ""
548+
}
549+
return cookie.Value
550+
}
551+
552+
// ClearSessionCookie expires the session_token cookie, effectively logging the user out on the browser side.
553+
// Call this after a successful Authenticator.Logout() call.
554+
//
555+
// Example:
556+
//
557+
// err := auth.Logout(r.Context(), req)
558+
// if err != nil { ... }
559+
// security.ClearSessionCookie(w)
560+
func ClearSessionCookie(w http.ResponseWriter, opts ...SessionCookieOptions) {
561+
var o SessionCookieOptions
562+
if len(opts) > 0 {
563+
o = opts[0]
564+
}
565+
566+
http.SetCookie(w, &http.Cookie{
567+
Name: o.name(),
568+
Value: "",
569+
Path: o.path(),
570+
Domain: o.Domain,
571+
MaxAge: -1,
572+
HttpOnly: true,
573+
Secure: o.secure(),
574+
SameSite: o.sameSite(),
575+
})
576+
}
577+
459578
// GetModelRulesFromContext extracts ModelRules stored by NewModelAuthMiddleware
460579
func GetModelRulesFromContext(ctx context.Context) (modelregistry.ModelRules, bool) {
461580
rules, ok := ctx.Value(ModelRulesKey).(modelregistry.ModelRules)

pkg/security/providers.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,9 +222,8 @@ func (a *DatabaseAuthenticator) Authenticate(r *http.Request) (*UserContext, err
222222

223223
if sessionToken == "" {
224224
// Try cookie
225-
cookie, err := r.Cookie("session_token")
226-
if err == nil {
227-
tokens = []string{cookie.Value}
225+
if token := GetSessionCookie(r); token != "" {
226+
tokens = []string{token}
228227
reference = "cookie"
229228
}
230229
} else {

0 commit comments

Comments
 (0)