Help us shape the future of Go! Take the 2026 Go Developer Survey

signed

package
v0.35.0 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package signed creates and verifies signed URLs: links that carry an expiry and an HMAC-SHA256 signature over their path and query, for email verification, unsubscribe links or temporary downloads.

key, err := app.DeriveKey("signed.urls", 32) // fails without APP_KEY
if err != nil {
	return err
}

s := signed.New(key).
	WithPreviousKeys(app.DerivePreviousKeys("signed.urls", 32)...).
	WithClock(app.Now)
link, _ := s.Sign(appURL+"/email/verify/42", time.Hour)
r.Get("/email/verify/{id}", verify).Use(s.Middleware())

Signer.Verify checks a request in constant time and returns ErrInvalid or ErrExpired; Signer.Middleware answers 403 for both. Signer.Token and Signer.Open sign and verify opaque payloads, such as pagination cursors, with the same keys.

See https://github.com/AlexGalhardo/vukem/blob/main/docs/security.md.

Example
package main

import (
	"bytes"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"time"

	"github.com/AlexGalhardo/vukem/signed"
)

func main() {
	// In an app, derive the key: app.Key.Derive("signed.urls", 32).
	s := signed.New(bytes.Repeat([]byte{3}, 32))

	link, err := s.Sign("https://example.com/email/verify/42", time.Hour)
	if err != nil {
		fmt.Println(err)
		return
	}

	ok := httptest.NewRequest(http.MethodGet, link, http.NoBody)
	fmt.Println(s.Verify(ok) == nil)

	tampered := httptest.NewRequest(http.MethodGet, strings.Replace(link, "/42", "/43", 1), http.NoBody)
	fmt.Println(s.Verify(tampered) == nil)
}
Output:
true
false

Index

Examples

Constants

View Source
const MinKeyLen = 32

MinKeyLen is the minimum signing key length; New panics below it.

Variables

View Source
var (
	ErrInvalid = &errs.HTTPError{Status: http.StatusForbidden, Message: "Invalid signature."}
	ErrExpired = &errs.HTTPError{Status: http.StatusForbidden, Message: "This link has expired."}
)

Errors returned by Verify (403 for clients).

Functions

This section is empty.

Types

type Signer

type Signer struct {
	// contains filtered or unexported fields
}

Signer signs URLs with one key and verifies them with that key or any previous key (APP_KEY rotation, see WithPreviousKeys).

func New

func New(key []byte) *Signer

New returns a Signer; derive key from APP_KEY (app.DeriveKey). It panics when key is shorter than MinKeyLen.

func (*Signer) Middleware

func (s *Signer) Middleware() func(http.Handler) http.Handler

Middleware rejects requests whose URL signature is missing, wrong or expired with 403 (and an "url.signature_invalid" security event).

func (*Signer) Open added in v0.34.0

func (s *Signer) Open(token string) ([]byte, error)

Open verifies a Token with the current or a previous key, in constant time, and returns its payload; any other string is ErrInvalid.

func (*Signer) Sign

func (s *Signer) Sign(rawURL string, ttl time.Duration) (string, error)

Sign adds "expires" (Unix seconds; ttl 0 means no expiry) and "signature" query parameters to rawURL. Only the path and query are signed, so links keep working behind proxies and on other hosts.

func (*Signer) Token added in v0.34.0

func (s *Signer) Token(payload []byte) string

Token signs payload into an opaque, URL-safe string "<base64url payload>.<base64url HMAC-SHA256>", for values a client hands back unchanged, such as pagination cursors. The payload is only encoded, not encrypted: never put a secret in it. A token does not expire; put a time in the payload when it must.

func (*Signer) Verify

func (s *Signer) Verify(r *http.Request) error

Verify checks the signature (constant time) and the expiry of r's URL.

func (*Signer) WithClock added in v0.28.0

func (s *Signer) WithClock(now func() time.Time) *Signer

WithClock makes s read the time from now (pass app.Now, so a vukemtest fake clock expires links); nil keeps the system clock. Call it before s is used.

func (*Signer) WithPreviousKeys added in v0.26.0

func (s *Signer) WithPreviousKeys(keys ...[]byte) *Signer

WithPreviousKeys makes s also accept links signed with keys (derive them with app.DerivePreviousKeys, from APP_PREVIOUS_KEYS), so links sent before an APP_KEY rotation keep working until the old key is removed. Sign always uses the current key. Call it before s is used; it panics when a key is shorter than MinKeyLen, like New.

Jump to

Keyboard shortcuts

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