jarm

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: 7 Imported by: 0

Documentation

Overview

Package jarm implements JWT Secured Authorization Response Mode signing and verification: encoding the authorization response as a signed JWT and validating one on receipt.

create.go is used by server, which produces the response JWT; verify.go is used by client, which must independently check issuer, audience, expiry and signature before trusting anything in the response. Create and Verify treat a success response and an error response identically — both are just a bag of top-level claims signed and checked the same way — which is what prevents a spoofed error response from getting weaker integrity protection than a success response.

As with internal/requestobject, only the JWT-standard claims (iss, aud, exp, nbf, iat) are parsed into typed fields; every actual authorization response parameter (code, state, error, error_description, error_uri, ...) is left as raw JSON in Parameters. This package has no notion of replay detection: an authorization response is correlated against the attempt it belongs to — and retired — by the client's storage.SessionStore, not by anything here.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrMalformedClaims indicates the response payload was not a JSON
	// object, or was missing a required top-level claim (iss, aud or
	// exp).
	ErrMalformedClaims = errors.New("jarm: malformed claims")

	// ErrIssuerMismatch indicates the response's iss claim did not equal
	// the authorization server the caller expected the response to come
	// from.
	ErrIssuerMismatch = errors.New("jarm: iss does not match expected issuer")

	// ErrAudienceMismatch indicates the response's aud claim did not
	// equal the caller's own client ID.
	ErrAudienceMismatch = errors.New("jarm: aud does not match expected audience")

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

	// ErrNotYetValid indicates the response's nbf claim is in the future
	// beyond the configured clock-skew tolerance.
	ErrNotYetValid = errors.New("jarm: response is not yet valid")

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

	// ErrNotAuthorizationResponse indicates a token that otherwise
	// verifies (signature, iss, aud, exp/nbf all check out) carries
	// neither a "code" nor an "error" parameter — so it isn't an
	// authorization response at all, even though it may be a validly
	// signed JWT from the same issuer for the same audience, such as an
	// ID token. The OpenID JARM spec defines no mandatory "typ" for the
	// response JWT, so this checks for the substance of an authorization
	// response instead of relying on a media type third-party
	// authorization servers aren't required to set.
	ErrNotAuthorizationResponse = errors.New("jarm: response carries neither a code nor an error parameter")
)

Functions

func Create

func Create(p CreateParams) (string, error)

Create builds and signs a JARM response for p. Success and error responses are created identically — Create has no notion of which this is, since both must be signed and verified with the same rigor.

Types

type Claims

type Claims struct {
	Issuer     string
	Audience   string
	ExpiresAt  time.Time
	IssuedAt   time.Time // zero if absent
	NotBefore  time.Time // zero if absent
	Parameters map[string]json.RawMessage
}

Claims is a parsed JARM response payload. iss, aud and exp are the JWT-standard claims JARM relies on; everything else — the actual authorization response parameters (code, state, error, error_description, error_uri, and any extension parameter) — is left in Parameters as raw JSON. This package treats a success response and an error response identically: both are signed the same way and validated the same way, which is what stops a spoofed error response from bypassing the integrity check a success response gets.

type CreateParams

type CreateParams struct {
	// Signer produces the response's signature.
	Signer crypto.Signer

	// Algorithm the response is signed with. Signer's key must match it.
	Algorithm fapi.SignatureAlgorithm

	// KeyID, if non-empty, is recorded in the response's "kid" header so
	// the verifier can select the right key from the server's published
	// JWKS without trial and error.
	KeyID string

	// Issuer is the "iss" claim — the authorization server's issuer
	// identifier.
	Issuer string

	// Audience is the "aud" claim — the client ID this response is
	// intended for.
	Audience string

	// Now is the response's issuance time.
	Now time.Time

	// Lifetime bounds how long the response is valid for (exp = Now +
	// Lifetime). JARM responses are meant to be consumed immediately, so
	// this should be short.
	Lifetime time.Duration

	// Parameters are the authorization response parameters to embed as
	// top-level claims — code and state for a success response, or
	// error, error_description and error_uri (plus state) for an error
	// response — each already encoded as JSON. Parameters must not use
	// the JWT-standard claim names iss, aud, exp, nbf or iat; Create
	// sets those itself.
	Parameters map[string]json.RawMessage
}

CreateParams describes one JARM response to create.

type Response

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

Response is a parsed, but not yet signature-verified, JARM response. KeyID, Algorithm, ClaimedIssuer and Parameter are available before Verify 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 Response, including an apparent error parameter, should be acted on until Verify returns a VerifiedResponse: an unverified "error" claim is exactly as untrustworthy as an unverified "code" claim.

func Parse

func Parse(token string) (Response, error)

Parse parses a JARM response without verifying its signature.

func (Response) Algorithm

func (r Response) Algorithm() fapi.SignatureAlgorithm

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

func (Response) ClaimedIssuer

func (r Response) ClaimedIssuer() string

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

func (Response) KeyID

func (r Response) KeyID() string

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

func (Response) Parameter

func (r Response) Parameter(name string) (json.RawMessage, bool)

Parameter returns the unverified authorization response parameter named name. Nothing derived from it — including an "error" or "code" parameter — should be acted on until Verify succeeds.

func (Response) Verify

func (r Response) Verify(pub crypto.PublicKey, policy VerifyPolicy) (VerifiedResponse, error)

Verify checks r's signature against pub and its claims against policy.

type VerifiedResponse

type VerifiedResponse struct {
	Issuer     string
	Parameters map[string]json.RawMessage
	ExpiresAt  time.Time
}

VerifiedResponse is what remains once a JARM response has been verified: the issuer it came from and the authorization response parameters it carried. Correlating those parameters against the authorization attempt they belong to (state, PKCE, expected redirect URI) is the caller's responsibility — see storage.SessionStore.

type VerifyPolicy

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

	// ExpectedAudience is the caller's own client ID. The response's aud
	// claim must equal it exactly.
	ExpectedAudience string

	// Algorithm is the algorithm this authorization server is registered
	// (or discovered) to sign JARM responses with. The response 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 response itself.
	Algorithm fapi.SignatureAlgorithm

	// Now is the time to validate exp/nbf against.
	Now time.Time

	// MaxLifetime bounds how far in the future (relative to Now) the
	// response's exp claim may be. Required — there is no implicit
	// default.
	MaxLifetime time.Duration

	// MaxClockSkew bounds how far in the future (relative to Now) an nbf
	// claim may be, and extends how long past exp a response is still
	// accepted. Zero means no tolerance.
	MaxClockSkew time.Duration
}

VerifyPolicy is the set of checks Verify enforces against a Response.

Jump to

Keyboard shortcuts

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