securecookie

package
v0.0.0-...-9754a49 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 11, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package securecookie is a very basic package for creating and reading secure cookies.

Index

Examples

Constants

View Source
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
)

KeyLength is the length of a key in bytes.

Variables

This section is empty.

Functions

func NewKey

func NewKey() []byte

NewKey generates a new random key of the correct length.

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

func (sk SingleKey) DecryptKeys() [][]byte

DecryptKeys implements the KeyProvider interface.

func (SingleKey) PrimaryKey

func (sk SingleKey) PrimaryKey() []byte

PrimaryKey implements the KeyProvider interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL