Documentation
¶
Overview ¶
Package securecookie is a very basic package for creating and reading secure cookies.
Index ¶
Examples ¶
Constants ¶
const ( // DefaultMaxAge is the default maximum age of a cookie in seconds. DefaultMaxAge = 30 * 24 * time.Hour // DefaultMaxLength is the default maximum length of an encoded value. // // 4093 bytes is chosen as a safe default based on the following: // - http://browsercookielimits.iain.guru/ // - https://chromestatus.com/feature/4946713618939904 DefaultMaxLength = 4093 )
const KeyLength = chacha20poly1305.KeySize
KeyLength is the length of a key in bytes.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type ExpiredError ¶
type ExpiredError struct {
// CookieExpiry is the time at which the cookie expired.
CookieExpiry time.Time
}
ExpiredError is the error type returned when a cookie is expired.
func (*ExpiredError) Error ¶
func (e *ExpiredError) Error() string
Error implements the error interface.
type KeyProvider ¶
type KeyProvider interface {
// PrimaryKey returns the primary key to be used for encryption and that
// will be tried first for decryption.
PrimaryKey() []byte
// DecryptKeys returns a slice of keys that will be tried when
// decrypting a cookie.
//
// This allows for key rotation, where old keys can still be used to
// decrypt cookies that were encrypted with them while new cookies are
// encrypted with the primary key.
DecryptKeys() [][]byte
}
KeyProvider is an interface for types that can provide key(s) to be used when encrypting and decrypting cookies.
All keys returned by a KeyProvider must random byte slices of length KeyLength.
All methods on a KeyProvider must be safe for concurrent use.
type Options ¶
type Options struct {
// MaxAge is the maximum age of the cookie. If MaxAge is 0, then
// DefaultMaxAge is used.
//
// To disable the MaxAge, set it to a negative value.
//
// The resolution of MaxAge is measured in milliseconds; any fractional
// milliseconds are truncated.
MaxAge time.Duration
// MaxLength is the maximum length of an encoded value. If MaxLength is 0,
// then DefaultMaxLength is used.
//
// Cookies that are longer than MaxLength will not be encoded and
// Encode will return an error.
MaxLength int
}
Options defines the options when creating a new SecureCookie.
type SecureCookie ¶
type SecureCookie[T any] struct { // contains filtered or unexported fields }
SecureCookie encodes and decodes authenticated and encrypted cookie values.
The generic type T is the type of the value that will be stored in the cookie. It must be JSON-serializable.
Example ¶
package main
import (
"crypto/rand"
"fmt"
"io"
"net/http"
"net/http/cookiejar"
"net/http/httptest"
"golang.org/x/net/publicsuffix"
)
func main() {
key := make([]byte, 32)
must(rand.Read(key))
type cookieData struct {
UserID int `json:"user_id"`
IsAdmin bool `json:"is_admin"`
}
s := must(New[cookieData](key))
const cookieName = "user"
mux := http.NewServeMux()
mux.HandleFunc("/set", func(w http.ResponseWriter, r *http.Request) {
// Set a cookie.
cookie := &cookieData{
UserID: 123,
IsAdmin: true,
}
if encoded, err := s.Encode(cookieName, cookie); err == nil {
http.SetCookie(w, &http.Cookie{
Name: cookieName,
Value: encoded,
Path: "/",
HttpOnly: true,
})
}
})
mux.HandleFunc("/get", func(w http.ResponseWriter, r *http.Request) {
// Get the cookie.
if cookie, err := r.Cookie(cookieName); err == nil {
var decoded cookieData
if err := s.Decode(cookieName, cookie.Value, &decoded); err == nil {
fmt.Fprintf(w, "UserID: %d, IsAdmin: %t", decoded.UserID, decoded.IsAdmin)
} else {
fmt.Fprintf(w, "Error decoding cookie: %v", err)
}
} else {
fmt.Fprintf(w, "Error getting cookie: %v", err)
}
})
// Use httptest to launch a test server; in the real world, you would
// use (*http.Server).ListenAndServe.
srv := httptest.NewServer(mux)
defer srv.Close()
client := srv.Client()
client.Jar = newCookieJar()
// Make a request to set the cookie.
resp := must(client.Get(srv.URL + "/set"))
if resp.StatusCode != http.StatusOK {
panic(fmt.Sprintf("unexpected status code: %d", resp.StatusCode))
}
resp.Body.Close()
// Make a request to get the cookie.
resp = must(client.Get(srv.URL + "/get"))
if resp.StatusCode != http.StatusOK {
panic(fmt.Sprintf("unexpected status code: %d", resp.StatusCode))
}
defer resp.Body.Close()
body := must(io.ReadAll(resp.Body))
// Print it!
fmt.Println(string(body))
}
func must[T any](v T, err error) T {
if err != nil {
panic(err)
}
return v
}
func newCookieJar() http.CookieJar {
jar, err := cookiejar.New(&cookiejar.Options{
PublicSuffixList: publicsuffix.List,
})
if err != nil {
panic(fmt.Sprintf("failed to create cookie jar: %v", err))
}
return jar
}
Output: UserID: 123, IsAdmin: true
func New ¶
func New[T any](key []byte) (*SecureCookie[T], error)
New returns a new SecureCookie, created using default options and a single key that will be used for both encryption and decryption.
func NewWith ¶
func NewWith[T any](keys KeyProvider, opts Options) (*SecureCookie[T], error)
NewWith returns a new SecureCookie with the given KeyProvider and options.
func (*SecureCookie[T]) Decode ¶
func (s *SecureCookie[T]) Decode(name, value string, dst *T) error
Decode decodes a cookie value.
It decodes, verifies, decrypts and finally deserializes the value.
The name argument is the cookie name. It must be the same name used when it was stored. The value argument is the encoded cookie value. The dst argument is where the cookie will be decoded.
func (*SecureCookie[T]) Encode ¶
func (s *SecureCookie[T]) Encode(name string, value *T) (string, error)
Encode encodes a cookie value.
It serializes, encrypts, and then encodes the value.
The name argument is the cookie name. It is stored with the encoded value. The value argument is the value to be encoded.
It is the client's responsibility to ensure that value, when encrypted and then base64-encoded, is shorter than the maximum permissible length.
type SingleKey ¶
type SingleKey []byte
SingleKey is a KeyProvider that returns a single key as the PrimaryKey and no DecryptKeys.
func (SingleKey) DecryptKeys ¶
DecryptKeys implements the KeyProvider interface.
func (SingleKey) PrimaryKey ¶
PrimaryKey implements the KeyProvider interface.