@@ -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
460579func GetModelRulesFromContext (ctx context.Context ) (modelregistry.ModelRules , bool ) {
461580 rules , ok := ctx .Value (ModelRulesKey ).(modelregistry.ModelRules )
0 commit comments