Skip to content

syntaqx/cookie

Repository files navigation

cookie

Go Go Reference Go Report Card codecov Mentioned in Awesome Go

Social Preview

Cookies, but with structs, for happiness.

Overview

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.

Features

  • 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.

Installation

go get github.com/syntaqx/cookie

Basic Usage

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.

Advanced Usage: Manager

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),
)

Setting Cookies

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})

Getting Cookies

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")

Populating Structs from Cookies

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.

Supporting Custom Types

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)
  }),
)