approvaltoken

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jun 4, 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 same config.Signing.HMACSecret used to sign X-E2A-Auth-* email headers is reused here, so there's 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 added in v0.3.0

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 added in v0.3.0

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 added in v0.3.0

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