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

signed

package
v0.30.0 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: MIT Imports: 11 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.

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