Cookies, but with structs, for happiness.
import (
"github.com/syntaqx/cookie"
)
...
type MyCookies struct {
Debug bool `cookie:"DEBUG"`
}
...
var cookies Cookies
err := cookie.PopulateFromCookies(r, &cookies)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Println(cookies.Debug)
For when you just want the value of the cookie:
debug, err := cookie.Get(r, "DEBUG")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
While it's very easy to set Cookies in Go, often times you'll be setting multiple cookies with the same options:
options := &cookie.Options{
Domain: "example.com",
Expires: time.Now().Add(24 * time.Hour),
MaxAge: 86400,
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteStrictMode,
}
cookie.Set(w, "debug", "true", options)
cookie.Set(w, "theme", "default", options)
cookie.Remove(w, "debug")
By default, cookies are stored in plaintext.
Cookies can be signed to ensure their value has not been tampered with. This works by creating a HMAC of the value (current cookie), and base64 encoding it. When the cookie gets read, it recalculates the signature and makes sure that it matches the signature attached to it.
It is still recommended that sensitive data not be stored in cookies, and that HTTPS be used to prevent cookie replay attacks.
If you want to sign your cookies, this can be accomplished by:
If you want to set a signed cookie, you can use the SetSigned
helper method:
cookie.SetSigned(w, "user_id", "123")
Alternatively, you can pass Signed
to the options when setting a cookie:
cookie.Set(w, "user_id", "123", &cookie.Options{
Signed: true,
})
These are functionally identical.
If you want to get a signed cookie, you can use the GetSigned
helper method:
userID, err := cookie.GetSigned(r, "user_id")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
To read signed cookies into your struct, you can use the signed
tag:
type User struct {
ID uuid.UUID `cookie:"user_id,signed"`
}
By default, the signing key is set to []byte(cookie.DefaultSigningKey)
. You
should change this signing key for your application by assigning the
cookie.SigningKey
variable to a secret value of your own:
cookie.SigningKey = []byte("my-secret-key")
You can set default options for all cookies by assigning the
cookie.DefaultOptions
variable:
cookie.DefaultOptions = &cookie.Options{
Domain: "example.com",
Expires: time.Now().Add(24 * time.Hour),
MaxAge: 86400,
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteStrictMode,
}
These options will be used as the defaults for cookies that do not strictly override them, allowing you to only set the values you care about.
If you want all cookies to be signed by default, you can set the Signed
field
in the cookie.DefaultOptions
:
cookie.DefaultOptions = &cookie.Options{
Signed: true,
}
Which will now sign all cookies by default when using the Set
method. You can
still override this by passing Signed: false
to the options when setting a
cookie.
cookie.Set(w, "debug", "true", &cookie.Options{
Signed: false,
})
This will require the use of the GetSigned
method to retrieve cookie values.
debug, err := cookie.GetSigned(r, "debug")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
When defaulting to signed cookies, unsigned cookies can still be populated by
using the unsigned
tag in the struct field:
type MyCookies struct {
Debug bool `cookie:"debug,unsigned"`
}
Or retrieved using the Get
method, which always retrieves the plaintext value:
debug, err := cookie.Get(r, "debug")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}