Documentation
¶
Index ¶
- Constants
- Variables
- type Claims
- type Manager
- func (m *Manager) Issue(audience, subject, email, clientID string, groups []string, ttl time.Duration, ...) (string, *Claims, error)
- func (m *Manager) OpenJSON(sealed string, v any, purpose string) error
- func (m *Manager) SealCount() uint64
- func (m *Manager) SealJSON(v any, purpose string) (string, error)
- func (m *Manager) SetLogger(l *zap.Logger)
- func (m *Manager) SetSealMetric(fn func(purpose string))
- func (m *Manager) Validate(tokenStr string) (*Claims, error)
Constants ¶
const ( PurposeClient = "client" PurposeSession = "session" PurposeCode = "code" PurposeAccess = "access" PurposeRefresh = "refresh" // PurposeConsent guards the short-lived blob that carries // validated /authorize parameters across the consent-page POST. // Distinct from PurposeSession so a sealed session cannot be // replayed as a consent token (or vice versa) — the AAD tag // enforces it. PurposeConsent = "consent" )
Purpose constants bind every sealed payload to a specific role via AEAD additional-data (AAD). A ciphertext minted with one purpose cannot be opened as any other, which closes the sealed-type confusion family without relying on JSON typ discriminators alone. The Typ field on each sealed struct is a belt-and-braces check layered on top of the AAD binding.
Variables ¶
var ErrTokenExpired = errors.New("token expired")
ErrTokenExpired is returned by Validate when the bearer's ExpiresAt has already passed. Sentinel — separate from the generic "validation failed" surface so the middleware can bucket expired-token traffic under a dedicated metric reason. Without this, operators can't tell "clients walked away / forgot to refresh" (benign, expected) from "attacker probing with forged tokens" (attack signal): both look like invalid_token spikes.
Functions ¶
This section is empty.
Types ¶
type Claims ¶
type Claims struct {
TokenID string `json:"tid"`
Typ string `json:"typ"`
Audience string `json:"aud"`
Resource string `json:"res,omitempty"`
Subject string `json:"sub"`
Email string `json:"email"`
Groups []string `json:"grp,omitempty"`
ClientID string `json:"cid"`
IssuedAt time.Time `json:"iat"`
ExpiresAt time.Time `json:"exp"`
}
Claims represents the internal access token payload.
Audience binds the token to the proxy base URL that issued it (cross-instance replay defense — two deployments sharing the same TOKEN_SIGNING_SECRET cannot honour each other's tokens).
Resource is the RFC 8707 resource indicator this token was minted for — for the single-mount proxy this is always {baseURL}{mountPath}. Sealed and validated separately from Audience so a future multi-mount or multi-tenant deployment that shares an origin cannot honour a token minted for another mount (RFC 8707 §2.2). Empty on tokens minted before this field existed; treated as "unknown" by the middleware (rejected only when the middleware was constructed with a non-empty expected resource — back-compat for callers that haven't opted in).
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager handles AES-GCM encryption for all stateless tokens and sealed payloads. All instances sharing the same secret can seal/open each other's payloads, enabling horizontal scaling without shared storage.
Multi-key rotation (G4.1): Manager supports one primary key (used for every Seal) and zero or more secondary keys (tried in order on Open, after the primary fails). A rolling key rotation looks like:
- Steady state: primary=A, secondaries=[].
- Rotation prepared: primary=B, secondaries=[A]. Tokens minted going forward are sealed with B; tokens minted before the rotation still decrypt because A remains in the try list.
- Bleed-in wait: at least one access-token TTL + one refresh- token TTL pass (1h + 7d ≈ 7d) so every in-flight A-sealed token either expires or rotates to a B-sealed one.
- Rotation complete: primary=B, secondaries=[]. A is safe to destroy.
Without this, a key rotation was a flag day — every existing session broke on the cutover.
func NewManager ¶
NewManager creates a token manager from a single signing secret (min 32 bytes). Equivalent to NewManagerWithRotation(secret) — kept for callers that do not need key rotation.
func NewManagerWithRotation ¶ added in v1.0.0
NewManagerWithRotation creates a token manager with one primary signing secret and zero or more secondary secrets. New payloads are always sealed with primary; Open tries primary first, then each secondary in the order given. Every secret must be at least 32 bytes. Duplicate secrets are allowed but wasted.
func (*Manager) Issue ¶
func (m *Manager) Issue(audience, subject, email, clientID string, groups []string, ttl time.Duration, resource string) (string, *Claims, error)
Issue creates a new opaque access token.
audience binds the token to a specific proxy deployment so it cannot be replayed against a sibling instance that happens to share the same signing secret.
resource is the RFC 8707 resource indicator this token was minted for. Sealed alongside audience so a future multi-mount deployment sharing the proxy origin cannot accept a token minted for a different mount (RFC 8707 §2.2). Pass "" when the caller does not participate in the resource binding (legacy / non-MCP callers).
func (*Manager) OpenJSON ¶
OpenJSON decrypts a sealed string and unmarshals the JSON payload into v. purpose must match the value passed to SealJSON.
func (*Manager) SealCount ¶ added in v1.0.0
SealCount returns the current number of successful seals. Exposed for tests and operator introspection; not part of the OAuth flow.
func (*Manager) SetLogger ¶ added in v1.0.0
SetLogger attaches a zap logger for the one-shot seal-rotation warning. Safe to call before any seals happen; passing nil disables the warning.
func (*Manager) SetSealMetric ¶ added in v1.0.0
SetSealMetric attaches a per-seal observer (typically a Prometheus CounterVec.WithLabelValues call) that the Manager invokes on every successful seal, labelled by purpose. Decouples the token package from the metrics package; main.go wires the actual counter at startup. Passing nil disables observation (tests).