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

approvaltoken

package
v1.3.0 Latest Latest
Warning

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

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

Documentation

Overview

Package approvaltoken signs and verifies the short-lived HMAC tokens embedded in HITL notification-email magic links.

A token authorizes exactly one action (approve or reject) on exactly one message and carries its own expiration. Tokens are URL-safe and contain no session state — verification depends only on the shared HMAC secret and the current time, so magic links work on any device the reviewer happens to be reading email on.

Format:

base64url(payload) + "." + base64url(hmac_sha256(secret, payload))

where payload is "<message_id>|<action>|<exp_unix>".

The shared config.Signing.HMACSecret is reused here, so there is no new key to rotate.

Index

Constants

View Source
const (
	ActionApprove = "approve"
	ActionReject  = "reject"
)

Action values mirror the design-doc contract. Any other value in a verified token is treated as tampering.

Variables

View Source
var (
	// ErrInvalidToken covers malformed, tampered, or unknown-action tokens.
	// Callers should treat all three the same — do not distinguish for
	// attackers.
	ErrInvalidToken = errors.New("invalid approval token")

	// ErrTokenExpired is returned when the signature is valid but the
	// embedded exp is in the past.
	ErrTokenExpired = errors.New("approval token expired")
)

Functions

func PeekMessageID

func PeekMessageID(token string) (string, error)

PeekMessageID extracts the message_id from a token *without* verifying the HMAC. Useful when the caller needs to look up the owning user's signing secrets to then call Verify with that secret list.

SECURITY: the returned message_id is attacker-controlled until Verify confirms the signature. Use it only as a lookup hint to find which secrets to verify against — never act on the value before Verify returns claims successfully.

func Sign

func Sign(secret, messageID, action string, exp time.Time) (string, error)

Sign returns a URL-safe token signed with `secret`. action must be ActionApprove or ActionReject. exp sets the token's expiration — callers should pass a value slightly after the message's approval_expires_at so a click received just before TTL still works.

Types

type Claims

type Claims struct {
	MessageID string
	Action    string
	ExpiresAt time.Time
}

Claims is the verified payload of a magic-link token.

func Verify

func Verify(secrets []string, token string) (*Claims, error)

Verify parses, HMAC-checks (against any of `secrets`), and exp-checks a token. Returns the claims on success; ErrInvalidToken for malformed / tampered / wrong-secret tokens; ErrTokenExpired for valid-but-past-exp tokens.

Verify does not check that the message still exists or is still pending — that is the handler's job. Verify's only job is "was this string issued by us, and is its exp in the future".

Accepting multiple secrets supports per-user multi-secret rotation: after a user creates a new secret, in-flight magic-link tokens issued under the old secret continue to verify until that secret is deleted.

type Signer

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

Signer is a thin single-secret wrapper kept for tests and the legacy deployment-wide signing path. New code should call Sign/Verify directly with the user's secrets pulled from the identity store.

func NewSigner

func NewSigner(secret string) *Signer

func (*Signer) Sign

func (s *Signer) Sign(messageID, action string, exp time.Time) (string, error)

func (*Signer) Verify

func (s *Signer) Verify(token string) (*Claims, error)

Jump to

Keyboard shortcuts

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