Cookies, but with structs, for happiness.
cookie
is a Go package designed to make handling HTTP cookies simple and
robust, and simplifying the process of parsing them into your structs. It
supports standard data types, custom data types, and signed cookies to ensure
data integrity.
- Easy to use: Simple API for managing cookies in your web applications.
- Struct-based cookie values: Easily get cookies into your structs.
- Custom type support: Extend cookie parsing with your own data types.
- Signed cookies: Ensure the integrity of your cookies with HMAC signatures.
- No external dependencies: Just pure standard library goodness.
go get github.com/syntaqx/cookie
The cookie
package provides a DefaultManager
that can be used to plug and
play into your existing applications:
cookie.Get(r, "DEBUG")
cookie.GetSigned(r, "Access-Token")
cookie.Set(w, "DEBUG", "true", cookie.Options{})
cookie.Set(w, "Access-Token", "token_value", cookie.Options{Signed: true})
cookie.SetSigned(w, "Access-Token", "token_value")
Or Populate a struct:
type RequestCookies struct {
Theme string `cookie:"THEME"`
Debug bool `cookie:"DEBUG,unsigned"`
AccessToken string `cookie:"Access-Token,signed"`
}
var c RequestCookies
cookie.PopulateFromCookies(r, &c)
In order to sign cookies however, you must provide a signing key:
signingKey := []byte("super-secret-key")
cookie.DefaultManager = cookie.NewManager(
cookie.WithSigningKey(signingKey),
)
Tip
Cookies are stored in plaintext by default (unsigned). A signed cookie is used to ensure the cookie value has not been tampered with. This is done by creating a HMAC signature of the cookie value using a secret key. Then, when the cookie is read, the signature is verified to ensure the cookie value has not been modified.
It is still recommended that sensitive data not be stored in cookies, and that HTTPS be used to prevent cookie replay attacks.
For more advanced usage, you can create a Manager
to handle your cookies,
rather than relying on the DefaultManager
:
manager := cookie.NewManager()
You can optionally provide a signing key for signed cookies:
signingKey := []byte("super-secret-key")
manager := cookie.NewManager(
cookie.WithSigningKey(signingKey),
)
Use the Set
method to set cookies. You can specify options such as path,
domain, expiration, and whether the cookie should be signed.
err := manager.Set(w, "DEBUG", "true", cookie.Options{})
err := manager.Set(w, "Access-Token", "token_value", cookie.Options{Signed: true})
Use the Get method to retrieve unsigned cookies and GetSigned for signed cookies.
value, err := manager.Get(r, "DEBUG")
value, err := manager.GetSigned(r, "Access-Token")
Use PopulateFromCookies
to populate a struct with cookie values. The struct
fields should be tagged with the cookie names.
type RequestCookies struct {
Theme string `cookie:"THEME"`
Debug bool `cookie:"DEBUG,unsigned"`
AccessToken string `cookie:"Access-Token,signed"`
NotRequired string `cookie:"NOT_REQUIRED,omitempty"`
}
var c RequestCookies
err := manager.PopulateFromCookies(r, &c)
Tip
By default, the PopulateFromCookies
method will return an error if a
required cookie is missing. You can use the omitempty
tag to make a field
optional.
To support custom types, register a custom handler with the Manager.
import (
"reflect"
"github.com/gofrs/uuid/v5"
"github.com/syntaqx/cookie"
)
...
manager := cookie.NewManager(
cookie.WithSigningKey(signingKey),
cookie.WithCustomHandler(reflect.TypeOf(uuid.UUID{}), func(value string) (interface{}, error) {
return uuid.FromString(value)
}),
)