Documentation
¶
Overview ¶
Package requestobject implements signed JAR (RFC 9101) request-object construction and verification for the parameters carried in an authorization/PAR request.
create.go is used by client to build the request object it pushes to the AS; verify.go is used by server to authenticate and parse it, including replay detection via a ReplayChecker. Create and Verify intentionally take independent parameter types (CreateParams and VerifyPolicy), even though they share JOSE encoding and claim-parsing helpers — signing policy (what the client is allowed to assert) and verification policy (what the server is willing to accept) are independent decisions that must be configurable independently.
Only the JWT-standard claims (iss, aud, exp, nbf, iat, jti) are parsed into typed fields. Every other top-level claim — the actual authorization request parameters, including any registered extension/RAR parameter — is left as raw JSON in Parameters for the extension package to interpret; this package has no opinion on which parameters are valid, only on the JWT envelope around them.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrWrongType indicates the object's "typ" header was present but // did not identify a request object (RFC 9101 §10.8) even after the // case-insensitive, "application/"-prefix-tolerant comparison // isRequestObjectType applies. An absent "typ" header is not an // error — see jwtType's doc comment. ErrWrongType = errors.New("requestobject: header typ is not oauth-authz-req+jwt") // ErrMalformedClaims indicates the payload was not a JSON object, or // was missing a required top-level claim (iss, aud or exp). ErrMalformedClaims = errors.New("requestobject: malformed claims") // ErrClientIDIssuerMismatch indicates the payload's "client_id" // parameter (RFC 9101 §5.3) did not equal its "iss" claim. ErrClientIDIssuerMismatch = errors.New("requestobject: client_id parameter does not match iss") // ErrIssuerMismatch indicates the object's iss claim did not equal // the client ID the caller expected to authenticate. ErrIssuerMismatch = errors.New("requestobject: iss does not match expected client ID") // ErrAudienceMismatch indicates the object's aud claim (a single // value or, per RFC 7519 §4.1.3, an array of values) did not contain // the audience (authorization server issuer identifier) the caller // expected. ErrAudienceMismatch = errors.New("requestobject: aud does not match expected audience") // ErrExpired indicates the object's exp claim is not after the // verification time. ErrExpired = errors.New("requestobject: object has expired") // ErrNotYetValid indicates the object's nbf claim is in the future // beyond the configured clock-skew tolerance. ErrNotYetValid = errors.New("requestobject: object is not yet valid") // ErrMissingNotBefore indicates VerifyPolicy.RequireNotBefore was set // but the object carries no nbf claim at all. ErrMissingNotBefore = errors.New("requestobject: nbf claim is required") // ErrLifetimeExceeded indicates the object's exp claim is further in // the future than the configured maximum lifetime allows. ErrLifetimeExceeded = errors.New("requestobject: exp exceeds maximum allowed lifetime") // ErrNotBeforeTooOld indicates the object's nbf claim is further in // the past than the configured maximum lifetime allows. Symmetric // with ErrLifetimeExceeded: that bounds how far exp may sit in the // future of Now, this bounds how far nbf may sit in the past of // Now — without it, an object with a normal, unexpired exp but an // ancient nbf would sail through both other checks despite claiming // an unreasonably long validity window, which is exactly what FAPI // 2.0 Message Signing Final §5.3.1 (FAPI2-MS-ID1-5.3.1-3) requires // be rejected. ErrNotBeforeTooOld = errors.New("requestobject: nbf exceeds maximum allowed age") )
Functions ¶
func Create ¶
func Create(p CreateParams) (string, error)
Create builds and signs a request object for p.
Types ¶
type Claims ¶
type Claims struct {
Issuer string
// Audience is the object's "aud" claim. RFC 7519 §4.1.3 permits a
// JWT's "aud" to be either a single string or an array of strings —
// both are normalized to this slice, so Verify's audience check
// treats a single value the same as a one-element array.
Audience []string
ExpiresAt time.Time
IssuedAt time.Time // zero if absent
NotBefore time.Time // zero if absent
JTI string // "" if absent
Parameters map[string]json.RawMessage
}
Claims is a parsed request object payload. iss, aud and exp are the JWT-standard claims RFC 9101 relies on; everything else — the actual authorization request parameters (response_type, client_id, redirect_uri, scope, state, nonce, code_challenge, code_challenge_method, authorization_details, and any registered extension parameter) — is left in Parameters as raw JSON for the extension/RAR layer to interpret against its own registered definitions. This package does not reject unrecognized parameter names; deciding which parameters are allowed is policy that belongs above this package.
type CreateParams ¶
type CreateParams struct {
// Signer produces the object's signature.
Signer crypto.Signer
// Algorithm the object is signed with. Signer's key must match it.
Algorithm fapi.SignatureAlgorithm
// KeyID, if non-empty, is recorded in the object's "kid" header so
// the verifier can select the right key from this client's
// registered JWKS without trial and error.
KeyID string
// ClientID is the "iss" claim (RFC 9101 §5.3). If Parameters
// contains a "client_id" entry, it must equal ClientID exactly.
ClientID string
// Audience is the "aud" claim — the authorization server issuer
// identifier this object is scoped to.
Audience string
// Now is the object's issuance time.
Now time.Time
// Lifetime bounds how long the object is valid for (exp = Now +
// Lifetime). FAPI profiles expect this to be short.
Lifetime time.Duration
// Random is the source of randomness for the object's "jti". If
// nil, crypto/rand.Reader is used.
Random io.Reader
// Parameters are the authorization request parameters to embed as
// top-level claims — response_type, client_id, redirect_uri, scope,
// state, nonce, code_challenge, code_challenge_method,
// authorization_details, and any registered extension parameter —
// each already encoded as JSON. Parameters must not use the
// JWT-standard claim names iss, aud, exp, nbf, iat or jti; Create
// sets those itself.
Parameters map[string]json.RawMessage
}
CreateParams describes one request object to create.
type Object ¶
type Object struct {
// contains filtered or unexported fields
}
Object is a parsed, but not yet signature-verified, request object. KeyID, Algorithm, ClaimedIssuer and Parameter are available before Verify succeeds so a caller can look up which registered client and key to verify against, and which client to attribute a rejected request to for error reporting — that is a safe use of unverified data, since it only selects what to check against, not what to trust. Nothing from Object should influence an authorization decision until Verify returns a VerifiedObject.
func (Object) Algorithm ¶
func (o Object) Algorithm() fapi.SignatureAlgorithm
Algorithm returns the algorithm the object 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 (Object) ClaimedIssuer ¶
ClaimedIssuer returns the object's unverified "iss" claim, for use as a client-lookup key only.
func (Object) KeyID ¶
KeyID returns the object header's "kid", or "" if absent. Untrusted until Verify succeeds; use only to select which key to verify against.
func (Object) Parameter ¶
func (o Object) Parameter(name string) (json.RawMessage, bool)
Parameter returns the unverified authorization request parameter named name, for use as a lookup key only (e.g. resolving a registered redirect_uri set before the signature has been checked). Nothing derived from it should influence an authorization decision until Verify succeeds.
func (Object) Verify ¶
func (o Object) Verify(ctx context.Context, pub crypto.PublicKey, policy VerifyPolicy) (VerifiedObject, error)
Verify checks o's signature against pub and its claims against policy.
type ReplayChecker ¶
type ReplayChecker interface {
UseOnce(ctx context.Context, jti string, expiresAt time.Time) error
}
ReplayChecker records that a request object's "jti" has been used, failing if it has been seen before. As with dpop.ReplayChecker and clientassertion.ReplayChecker, implementations are expected to key storage by a namespaced digest of jti, not the raw value.
type VerifiedObject ¶
type VerifiedObject struct {
ClientID string
Parameters map[string]json.RawMessage
ExpiresAt time.Time
}
VerifiedObject is what remains once a request object has been verified: the authenticated client ID and the authorization request parameters it carried.
type VerifyPolicy ¶
type VerifyPolicy struct {
// ExpectedClientID is the client the caller is trying to
// authenticate. The object's iss claim must equal it exactly.
ExpectedClientID string
// ExpectedAudience is the authorization server issuer identifier the
// object's aud claim must equal exactly.
ExpectedAudience string
// Algorithm is the algorithm this client is registered to sign
// request objects with. The object header's algorithm must equal it
// exactly — this is what prevents algorithm-confusion attacks, so it
// must come from the client's registration, never from the object
// 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
// object's exp claim may be, and symmetrically how far in the past
// its nbf claim may be — an object is meant to represent one short,
// coherent validity window around when it was created, not two
// independently-bounded claims, so the same limit governs both
// directions. 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 an object is still
// accepted. Zero means no tolerance.
MaxClockSkew time.Duration
// RequireNotBefore rejects an object with no nbf claim at all,
// rather than simply skipping the not-yet-valid check for it. FAPI
// 2.0 Message Signing Final §5.3.1 mandates nbf ("shall require the
// request object to contain an nbf claim"); the base FAPI 2.0
// Security Profile does not (it relies on request_uri's own short
// lifetime instead, per its "Main differences to FAPI 1.0" table) —
// so the caller sets this only under
// ProfileFAPISecurityWithMessageSigning, not unconditionally.
RequireNotBefore bool
// Replay, if non-nil, is used to detect object replay by jti — but
// only when the object actually carries one. Neither RFC 9101 nor
// FAPI 2.0 Message Signing Final requires a request object to
// include jti (message-signing's own replay defense is its
// mandatory, tightly-bounded nbf/exp window, not a jti claim), and
// the OIDF conformance suite's own request objects never carry one
// under the plain FAPI2 profile — only its Brazil-specific profile
// behavior adds one. So a missing jti here is not an error; it just
// means this particular object skips replay-by-jti, the same as a
// nil Replay would. This is fine architecturally because — when a
// request object is only ever accepted via a PAR request_uri that is
// itself single-use — replay detection here was always meant as
// defense in depth, not the primary control.
Replay ReplayChecker
}
VerifyPolicy is the set of checks Verify enforces against an Object.