token

package
v0.19.0 Latest Latest
Warning

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

Go to latest
Published: Aug 29, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package token implements shared JWT access token (RFC 9068) and ID token (OIDC Core) issuance and validation logic that sits below the public TokenSet/TokenResult types.

issue.go is used by server to mint tokens, including DPoP sender constraint binding via a cnf.jkt claim; validate.go is used by client (validating an ID token it received from the token endpoint) and by resource (validating a presented JWT access token's signature, issuer, audience and lifetime). AccessToken.Validate exposes a verified token's cnf.jkt claim (ValidatedAccessToken.JKT) rather than checking it against an expected value itself — DPoP sender-constraint binding, ordinary expiry and revocation are resource.Verify()'s own job, enforced once, uniformly, for every access-token format resource.AccessTokenResolver supports, not just JWT (see that interface's doc comment in resource/accesstoken.go). Each token kind's Parse and Validate stay separate types from its Issue, even though they interpret the same claim set, because a server issuing a token and a verifier checking one make different trust assumptions about where the token came from.

Refresh tokens are not covered here: this module treats them as opaque, storage-backed credentials issued and redeemed by storage.GrantStore, not as JWTs with claims to parse.

As with internal/requestobject and internal/jarm, only the claims each token kind's governing spec defines are parsed into typed fields; anything else — most notably a granted authorization_details (RFC 9396) reflected into an access token — is left as raw JSON in Parameters.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrWrongType indicates an access token header's "typ" was not
	// "at+jwt" (RFC 9068 §2.1).
	ErrWrongType = errors.New("token: header typ is not at+jwt")

	// ErrMalformedClaims indicates a payload was not a JSON object, was
	// missing a required top-level claim, or had a claim of the wrong
	// shape (including a "cnf" claim that was not a well-formed
	// DPoP-thumbprint confirmation).
	ErrMalformedClaims = errors.New("token: malformed claims")

	// ErrIssuerMismatch indicates a token's iss claim did not equal the
	// issuer the caller expected.
	ErrIssuerMismatch = errors.New("token: iss does not match expected issuer")

	// ErrAudienceMismatch indicates a token's aud claim did not contain
	// the audience the caller expected, or — for an ID token — named an
	// additional audience the caller's IDTokenValidatePolicy.
	// TrustedAudiences doesn't list (OIDC Core §3.1.3.7 step 3).
	ErrAudienceMismatch = errors.New("token: aud does not match expected audience")

	// ErrExpired indicates a token's exp claim is not after the
	// verification time.
	ErrExpired = errors.New("token: token has expired")

	// ErrLifetimeExceeded indicates a token's exp claim is further in
	// the future than the configured maximum lifetime allows.
	ErrLifetimeExceeded = errors.New("token: exp exceeds maximum allowed lifetime")

	// ErrNonceMismatch indicates an ID token's nonce claim did not match
	// the nonce the caller's authorization request carried.
	ErrNonceMismatch = errors.New("token: nonce does not match expected value")

	// ErrMissingAuthorizedParty indicates an ID token's aud claim named
	// more than one audience but carried no azp claim (OIDC Core
	// §3.1.3.7 step 9) — needed to disambiguate which of those
	// audiences the token was actually authorized for.
	ErrMissingAuthorizedParty = errors.New("token: aud has multiple audiences but azp is missing")

	// ErrAuthorizedPartyMismatch indicates an ID token's azp claim did
	// not equal the caller's own client ID (OIDC Core §3.1.3.7 step 10).
	ErrAuthorizedPartyMismatch = errors.New("token: azp does not match expected audience")

	// ErrIssuedAtTooOld indicates an ID token's iat claim is further in
	// the past than the configured maximum lifetime allows — OIDC Core
	// §3.1.3.7 step 10: "iat... can be used to reject tokens that were
	// issued too far away from the current time." Mirrors
	// ErrLifetimeExceeded's own bound on exp, applied to the opposite
	// direction around Now.
	ErrIssuedAtTooOld = errors.New("token: iat exceeds maximum allowed age")
)

Functions

func IssueAccessToken

func IssueAccessToken(p AccessTokenParams) (token string, jti string, err error)

IssueAccessToken builds and signs a JWT access token for p, returning the compact JWT and the "jti" claim embedded in it — callers that need to later revoke this specific token (e.g. on detected authorization-code reuse, RFC 6749 §4.1.2) need the jti; previously generated internally and discarded.

func IssueIDToken

func IssueIDToken(p IDTokenParams) (string, error)

IssueIDToken builds and signs an ID token for p.

Types

type AccessToken

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

AccessToken is a parsed, but not yet signature-verified, JWT access token. KeyID, Algorithm and ClaimedIssuer are available before Validate succeeds so a caller can look up which key to verify against — that is a safe use of unverified data, since it only selects what to check against, not what to trust. Nothing from AccessToken, including its scope or confirmation claim, should influence an authorization decision until Validate returns a ValidatedAccessToken.

func ParseAccessToken

func ParseAccessToken(tok string) (AccessToken, error)

ParseAccessToken parses a JWT access token without verifying its signature.

func (AccessToken) Algorithm

func (t AccessToken) Algorithm() fapi.SignatureAlgorithm

Algorithm returns the algorithm the token header claims to use. Untrusted until Validate succeeds — callers must still supply the algorithm they expect via AccessTokenValidatePolicy rather than trusting this value, exactly as jose.Compact.Verify requires.

func (AccessToken) ClaimedIssuer

func (t AccessToken) ClaimedIssuer() string

ClaimedIssuer returns the token's unverified "iss" claim, for use as a key-lookup hint only.

func (AccessToken) KeyID

func (t AccessToken) KeyID() string

KeyID returns the token header's "kid", or "" if absent. Untrusted until Validate succeeds; use only to select which key to verify against.

func (AccessToken) Validate

Validate checks t's signature against pub and its claims against policy.

type AccessTokenClaims

type AccessTokenClaims struct {
	Issuer       string
	Subject      string
	Audience     []string
	ClientID     string
	Scope        string // space-delimited; "" if absent
	ExpiresAt    time.Time
	IssuedAt     time.Time
	JTI          string
	Confirmation *Confirmation // nil if the token is not sender-constrained
	Parameters   map[string]json.RawMessage
}

AccessTokenClaims is a parsed JWT access token payload (RFC 9068). Audience is a slice because RFC 9068 §3 does not narrow RFC 7519 §4.1.3's general "aud" definition, which permits either a single string or an array — unlike internal/clientassertion's own Audience, which stays a single string because a client assertion is always addressed to exactly one token endpoint, never several audiences at once. Everything beyond the claims RFC 9068 defines — most importantly a granted authorization_details (RFC 9396) — is left in Parameters as raw JSON.

type AccessTokenParams

type AccessTokenParams struct {
	Signer    crypto.Signer
	Algorithm fapi.SignatureAlgorithm
	KeyID     string

	Issuer   string
	Subject  string
	Audience string
	ClientID string
	Scope    string // "" to omit

	// Confirmation binds the token to a DPoP key or an mTLS client
	// certificate by thumbprint (exactly one of Confirmation.JKT/X5TS256
	// set, never both). Leave nil to issue a bearer (non-sender-constrained)
	// token — whether that's acceptable is a policy decision made above
	// this package.
	Confirmation *Confirmation

	Now      time.Time
	Lifetime time.Duration

	// Random is the source of randomness for the token's "jti". If nil,
	// crypto/rand.Reader is used.
	Random io.Reader

	// Parameters are additional top-level claims to embed — most
	// notably a granted authorization_details (RFC 9396) — each already
	// encoded as JSON. Parameters must not use the reserved claim names
	// this package manages itself.
	Parameters map[string]json.RawMessage
}

AccessTokenParams describes one access token to issue.

type AccessTokenValidatePolicy

type AccessTokenValidatePolicy struct {
	// ExpectedIssuer is the authorization server the caller expects this
	// token to have come from. The token's iss claim must equal it
	// exactly.
	ExpectedIssuer string

	// ExpectedAudience is the resource the caller expects this token to
	// be scoped to. The token's aud claim must equal it exactly.
	ExpectedAudience string

	// Algorithm is the algorithm this authorization server is
	// registered (or discovered) to sign access tokens with. The token
	// header's algorithm must equal it exactly — this is what prevents
	// algorithm-confusion attacks, so it must come from the server's
	// metadata, never from the token itself.
	Algorithm fapi.SignatureAlgorithm

	Now         time.Time
	MaxLifetime time.Duration
}

AccessTokenValidatePolicy is the set of checks Validate enforces against an AccessToken.

type Confirmation

type Confirmation struct {
	JKT     string
	X5TS256 string
}

Confirmation is the RFC 7800 "cnf" claim used to record a sender constraint on an access token — exactly one of JKT (DPoP key thumbprint, RFC 9449 §6.1) or X5TS256 (mTLS certificate thumbprint, RFC 8705 §3.1) is ever set on a given token, never both: a token is bound exactly one way.

type IDToken

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

IDToken is a parsed, but not yet signature-verified, ID token. As with AccessToken, KeyID/Algorithm/ClaimedIssuer are safe to use as lookup hints before Validate succeeds, but nothing else should be trusted until then.

func ParseIDToken

func ParseIDToken(tok string) (IDToken, error)

ParseIDToken parses an ID token without verifying its signature, rejecting one longer than jose.DefaultMaxCompactBytes.

func ParseIDTokenMax added in v0.14.0

func ParseIDTokenMax(tok string, maxBytes int) (IDToken, error)

ParseIDTokenMax is ParseIDToken with an explicit size ceiling, in bytes, instead of jose.DefaultMaxCompactBytes — for a caller whose issuer may legitimately return an ID token shaped by however many scopes/claims it granted, rather than a fixed handful.

func (IDToken) Algorithm

func (t IDToken) Algorithm() fapi.SignatureAlgorithm

Algorithm returns the algorithm the token header claims to use. Untrusted until Validate succeeds.

func (IDToken) ClaimedIssuer

func (t IDToken) ClaimedIssuer() string

ClaimedIssuer returns the token's unverified "iss" claim, for use as a key-lookup hint only.

func (IDToken) KeyID

func (t IDToken) KeyID() string

KeyID returns the token header's "kid", or "" if absent. Untrusted until Validate succeeds; use only to select which key to verify against.

func (IDToken) Validate

Validate checks t's signature against pub and its claims against policy.

type IDTokenClaims

type IDTokenClaims struct {
	Issuer    string
	Subject   string
	Audience  []string
	ExpiresAt time.Time
	IssuedAt  time.Time
	Nonce     string    // "" if absent
	AuthTime  time.Time // zero if absent
	ACR       string    // "" if absent
	AMR       []string  // nil if absent
	AZP       string    // "" if absent

	Parameters map[string]json.RawMessage
}

IDTokenClaims is a parsed ID token payload (OIDC Core §2).

Audience is a slice because OIDC Core §2 explicitly documents "aud" as possibly multi-valued for an ID token ("In the general case, the aud value is an array of case sensitive strings" — a single string is just the one-element case) — unlike internal/clientassertion's own Audience, which stays a single string because a client assertion is always addressed to exactly one token endpoint, never several audiences at once. §3.1.3.7 step 9 additionally says a client "SHOULD verify that an azp Claim is present" when there's more than one audience, and step 10 that it "SHOULD verify" azp equals the client's own ID when present — IDTokenValidatePolicy.Validate enforces both.

Everything beyond the claims this struct names is left in Parameters as raw JSON.

type IDTokenParams

type IDTokenParams struct {
	Signer    crypto.Signer
	Algorithm fapi.SignatureAlgorithm
	KeyID     string

	Issuer   string
	Subject  string
	Audience string

	Nonce    string    // "" to omit
	AuthTime time.Time // zero to omit
	ACR      string    // "" to omit
	AMR      []string  // nil to omit

	Now      time.Time
	Lifetime time.Duration

	// Parameters are additional top-level claims to embed, each already
	// encoded as JSON. Parameters must not use the reserved claim names
	// this package manages itself.
	Parameters map[string]json.RawMessage
}

IDTokenParams describes one ID token to issue.

type IDTokenValidatePolicy

type IDTokenValidatePolicy struct {
	// ExpectedIssuer is the authorization server the caller expects this
	// token to have come from. The token's iss claim must equal it
	// exactly.
	ExpectedIssuer string

	// ExpectedAudience is the caller's own client ID. The token's aud
	// claim — a single string or, per OIDC Core §2, an array — must
	// contain it.
	ExpectedAudience string

	// TrustedAudiences lists any other party the caller trusts to also
	// be named alongside ExpectedAudience in a multi-valued aud. OIDC
	// Core §3.1.3.7 step 3 requires rejecting an ID token "if it
	// contains additional audiences not trusted by the Client" — by
	// default (nil/empty) this package trusts none, so every element of
	// aud besides ExpectedAudience causes rejection, preserving the
	// exact-match behavior this package has always had. Set only to
	// entries the caller has an actual, specific reason to trust.
	TrustedAudiences []string

	// Algorithm is the algorithm this authorization server is
	// registered (or discovered) to sign ID tokens with. The token
	// header's algorithm must equal it exactly.
	Algorithm fapi.SignatureAlgorithm

	// ExpectedNonce, if non-empty, requires the token's nonce claim to
	// equal it exactly — this is what binds the ID token back to the
	// specific authorization request that requested it. Leave empty
	// only when the authorization request itself carried no nonce.
	ExpectedNonce string

	Now          time.Time
	MaxLifetime  time.Duration
	MaxClockSkew time.Duration
}

IDTokenValidatePolicy is the set of checks Validate enforces against an IDToken.

type ValidatedAccessToken

type ValidatedAccessToken struct {
	Subject    string
	ClientID   string
	Scope      string
	Parameters map[string]json.RawMessage
	ExpiresAt  time.Time

	// JTI is the token's "jti" claim, now trusted — the signature has
	// been verified by this point, unlike AccessToken.KeyID()/
	// ClaimedIssuer(), which are documented as pre-verification lookup
	// hints only. A caller can use this to check token-specific
	// revocation (RFC 6750 §3.1's invalid_token case).
	JTI string

	// JKT is the token's "cnf.jkt" claim, now trusted, or "" if the
	// token carried no confirmation claim at all (or was bound via
	// X5TS256 instead — the two are mutually exclusive, see
	// Confirmation's own doc comment). This package no longer checks it
	// against an expected value itself (see this struct's — and
	// AccessTokenValidatePolicy's — history: that check moved to the
	// resource package's Verify(), which enforces sender-constraint
	// binding once, uniformly, regardless of access-token format).
	// Callers that need sender-constraint enforcement must compare this
	// themselves.
	JKT string

	// X5TS256 is the token's "cnf.x5t#S256" claim (RFC 8705 §3.1), now
	// trusted, or "" if the token was bound via JKT instead (or not
	// bound at all). Mirrors JKT's own contract exactly, for mTLS
	// binding instead of DPoP.
	X5TS256 string
}

ValidatedAccessToken is what remains once an access token has been validated.

type ValidatedIDToken

type ValidatedIDToken struct {
	Subject    string
	AuthTime   time.Time // zero if the token carried no auth_time
	ACR        string
	AMR        []string
	Parameters map[string]json.RawMessage
	ExpiresAt  time.Time

	// IssuedAt is the token's "iat" — required to be present and
	// well-formed for parsing to succeed at all, and bounded by
	// MaxLifetime the same way ExpiresAt is (see Validate's own iat
	// check). Exposed for a caller's own telemetry or cross-checks
	// beyond that one bound.
	IssuedAt time.Time
}

ValidatedIDToken is what remains once an ID token has been validated. AuthTime, ACR and AMR are exposed for the caller to apply its own freshness/assurance policy (e.g. a requested max_age or acr_values) — this package only checks what it can check generically.

Jump to

Keyboard shortcuts

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