Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Session Only Cookies #1752

Merged
merged 7 commits into from Feb 7, 2022
28 changes: 17 additions & 11 deletions ctx.go
Expand Up @@ -75,15 +75,16 @@ type Range struct {

// Cookie data for c.Cookie
type Cookie struct {
Name string `json:"name"`
Value string `json:"value"`
Path string `json:"path"`
Domain string `json:"domain"`
MaxAge int `json:"max_age"`
Expires time.Time `json:"expires"`
Secure bool `json:"secure"`
HTTPOnly bool `json:"http_only"`
SameSite string `json:"same_site"`
Name string `json:"name"`
Value string `json:"value"`
Path string `json:"path"`
Domain string `json:"domain"`
MaxAge int `json:"max_age"`
Expires time.Time `json:"expires"`
Secure bool `json:"secure"`
HTTPOnly bool `json:"http_only"`
SameSite string `json:"same_site"`
SessionOnly bool `json:"session_only"`
}

// Views is the interface that wraps the Render function.
Expand Down Expand Up @@ -410,8 +411,13 @@ func (c *Ctx) Cookie(cookie *Cookie) {
fcookie.SetValue(cookie.Value)
fcookie.SetPath(cookie.Path)
fcookie.SetDomain(cookie.Domain)
fcookie.SetMaxAge(cookie.MaxAge)
fcookie.SetExpire(cookie.Expires)
// only set max age and expiry when SessionOnly is false
// i.e. cookie supposed to last beyond browser session
// refer: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#define_the_lifetime_of_a_cookie
if !cookie.SessionOnly {
fcookie.SetMaxAge(cookie.MaxAge)
fcookie.SetExpire(cookie.Expires)
}
fcookie.SetSecure(cookie.Secure)
fcookie.SetHTTPOnly(cookie.HTTPOnly)

Expand Down
8 changes: 8 additions & 0 deletions ctx_test.go
Expand Up @@ -659,6 +659,14 @@ func Test_Ctx_Cookie(t *testing.T) {
cookie.SameSite = CookieSameSiteNoneMode
c.Cookie(cookie)
utils.AssertEqual(t, expect, string(c.Response().Header.Peek(HeaderSetCookie)))

expect = "username=john; path=/; secure; SameSite=None"
// should remove expires and max-age headers
cookie.SessionOnly = true
cookie.Expires = expire
cookie.MaxAge = 10000
c.Cookie(cookie)
utils.AssertEqual(t, expect, string(c.Response().Header.Peek(HeaderSetCookie)))
}

// go test -v -run=^$ -bench=Benchmark_Ctx_Cookie -benchmem -count=4
Expand Down
4 changes: 4 additions & 0 deletions middleware/csrf/README.md
Expand Up @@ -109,6 +109,10 @@ type Config struct {
// Optional. Default value "Lax".
CookieSameSite string

// Decides whether cookie should last for only the browser sesison.
// Ignores Expiration if set to true
CookieSessionOnly bool

// Expiration is the duration before csrf token will expire
//
// Optional. Default: 1 * time.Hour
Expand Down
4 changes: 4 additions & 0 deletions middleware/csrf/config.go
Expand Up @@ -53,6 +53,10 @@ type Config struct {
// Optional. Default value "Lax".
CookieSameSite string

// Decides whether cookie should last for only the browser sesison.
// Ignores Expiration if set to true
CookieSessionOnly bool

// Expiration is the duration before csrf token will expire
//
// Optional. Default: 1 * time.Hour
Expand Down
32 changes: 17 additions & 15 deletions middleware/csrf/csrf.go
Expand Up @@ -48,13 +48,14 @@ func New(config ...Config) fiber.Handler {
if manager.getRaw(token) == nil {
// Expire cookie
c.Cookie(&fiber.Cookie{
Name: cfg.CookieName,
Domain: cfg.CookieDomain,
Path: cfg.CookiePath,
Expires: time.Now().Add(-1 * time.Minute),
Secure: cfg.CookieSecure,
HTTPOnly: cfg.CookieHTTPOnly,
SameSite: cfg.CookieSameSite,
Name: cfg.CookieName,
Domain: cfg.CookieDomain,
Path: cfg.CookiePath,
Expires: time.Now().Add(-1 * time.Minute),
Secure: cfg.CookieSecure,
HTTPOnly: cfg.CookieHTTPOnly,
SameSite: cfg.CookieSameSite,
SessionOnly: cfg.CookieSessionOnly,
})
return cfg.ErrorHandler(c, errTokenNotFound)
}
Expand All @@ -71,14 +72,15 @@ func New(config ...Config) fiber.Handler {

// Create cookie to pass token to client
cookie := &fiber.Cookie{
Name: cfg.CookieName,
Value: token,
Domain: cfg.CookieDomain,
Path: cfg.CookiePath,
Expires: time.Now().Add(cfg.Expiration),
Secure: cfg.CookieSecure,
HTTPOnly: cfg.CookieHTTPOnly,
SameSite: cfg.CookieSameSite,
Name: cfg.CookieName,
Value: token,
Domain: cfg.CookieDomain,
Path: cfg.CookiePath,
Expires: time.Now().Add(cfg.Expiration),
Secure: cfg.CookieSecure,
HTTPOnly: cfg.CookieHTTPOnly,
SameSite: cfg.CookieSameSite,
SessionOnly: cfg.CookieSessionOnly,
}
// Set cookie to response
c.Cookie(cookie)
Expand Down