dpop

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

Documentation

Overview

Package dpop implements DPoP (RFC 9449) proof creation and verification: proof JWT construction, the "ath" access-token hash, JWK thumbprint computation, and the checks needed to detect proof replay.

create.go is used by client (to prove possession of its DPoP key on requests to the AS and to resource servers); verify.go is used by both server and resource, since both roles must independently validate a proof presented to them rather than trusting a previous validation.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrWrongType indicates the proof's "typ" header was not "dpop+jwt".
	ErrWrongType = errors.New("dpop: header typ is not dpop+jwt")

	// ErrMissingJWK indicates the proof's header had no embedded "jwk".
	ErrMissingJWK = errors.New("dpop: header is missing jwk")

	// ErrMethodMismatch indicates the proof's "htm" claim did not match
	// the HTTP method of the request it was presented with.
	ErrMethodMismatch = errors.New("dpop: htm does not match request method")

	// ErrURIMismatch indicates the proof's "htu" claim did not match the
	// canonicalized target URI of the request it was presented with.
	ErrURIMismatch = errors.New("dpop: htu does not match request URI")

	// ErrIssuedInFuture indicates the proof's "iat" claim is further in
	// the future than the configured clock-skew tolerance allows.
	ErrIssuedInFuture = errors.New("dpop: iat is in the future")

	// ErrExpired indicates the proof's "iat" claim is older than the
	// configured maximum proof age.
	ErrExpired = errors.New("dpop: proof has expired")

	// ErrAccessTokenHashMismatch indicates the proof's "ath" claim did
	// not match the presented access token, or was present/absent when
	// the opposite was expected.
	ErrAccessTokenHashMismatch = errors.New("dpop: ath does not match access token")

	// ErrNonceMismatch indicates the proof's "nonce" claim did not match
	// the nonce the verifier requires.
	ErrNonceMismatch = errors.New("dpop: nonce does not match required value")

	// ErrMalformedClaims indicates the proof payload was missing a
	// required claim (jti, htm or htu) or contained an unrecognized one.
	ErrMalformedClaims = errors.New("dpop: malformed claims")
)

Functions

func CreateProof

func CreateProof(req ProofRequest) (string, error)

CreateProof builds and signs a DPoP proof JWT for req.

Types

type ProofRequest

type ProofRequest struct {
	// Signer produces the proof's signature. Its public key is embedded
	// in the proof header as the "jwk" member.
	Signer crypto.Signer

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

	// Method is the HTTP method of the request this proof is bound to.
	Method string

	// URL is the target URI of the request this proof is bound to. It is
	// canonicalized into the "htu" claim; query and fragment are
	// stripped per RFC 9449 §4.2.
	URL *url.URL

	// AccessToken, if non-empty, is hashed into the proof's "ath" claim
	// (RFC 9449 §4.3). Leave empty when creating a proof for a token
	// request, where no access token exists yet.
	AccessToken string

	// Nonce, if non-empty, is echoed into the proof's "nonce" claim in
	// response to a server-provided DPoP-Nonce challenge.
	Nonce string

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

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

ProofRequest describes one DPoP proof to create.

type ReplayChecker

type ReplayChecker interface {
	UseOnce(ctx context.Context, jti string, expiresAt time.Time) error
}

ReplayChecker records that a DPoP proof's "jti" has been used, failing if it has been seen before. Implementations are expected to key storage by a namespaced digest of jti, not the raw value — see storage.ReplayStore — but that adaptation is the caller's responsibility so this package stays decoupled from a specific storage contract.

type VerifiedProof

type VerifiedProof struct {
	Thumbprint jose.Thumbprint
	IssuedAt   time.Time
	Nonce      string
}

VerifiedProof is everything about a DPoP proof worth retaining once it has been verified: the thumbprint of the key it was signed with (for binding against a token's cnf.jkt), when it was issued, and the proof's own "nonce" claim (empty if absent).

Nonce is handed back unconditionally, regardless of RequiredNonce — unlike RequiredNonce's own compare-to-one-known-value check, a caller implementing single-use, issued-per-challenge nonces (RFC 9449 §8, §9) doesn't know the expected value in advance; it has to look up whether the presented value was one it actually issued, which only it (not this package) can do.

func Verify

func Verify(ctx context.Context, req VerifyRequest) (VerifiedProof, error)

Verify checks a DPoP proof against req. On success, the returned VerifiedProof's Thumbprint identifies the key that produced it — the caller is responsible for checking that thumbprint against whatever it expects (e.g. a token's cnf.jkt), since what counts as "expected" differs between the authorization server issuing a token and a resource server checking one.

type VerifyRequest

type VerifyRequest struct {
	// Proof is the compact JWS presented in the DPoP header.
	Proof string

	// Method and URL are the HTTP method and target URI of the request
	// the proof was presented with. The proof's "htm"/"htu" claims must
	// match them exactly (after canonicalization).
	Method string
	URL    *url.URL

	// AccessToken, if non-empty, is the access token presented alongside
	// this proof. When set, the proof's "ath" claim must match its hash;
	// when empty, the proof must not carry an "ath" claim at all.
	AccessToken string

	// RequiredNonce, if non-empty, is the nonce this verifier previously
	// issued; the proof's "nonce" claim must match it exactly.
	RequiredNonce string

	// Now is the time to validate the proof's "iat" against.
	Now time.Time

	// MaxProofAge is how old (relative to Now) a proof's "iat" may be
	// before it is rejected. Required — there is no implicit default.
	MaxProofAge time.Duration

	// MaxClockSkew bounds how far in the future (relative to Now) a
	// proof's "iat" may be before it is rejected. Zero means no
	// tolerance for a future-dated proof.
	MaxClockSkew time.Duration

	// Replay, if non-nil, is used to detect proof replay by "jti". A nil
	// Replay skips replay detection — callers verifying in a context
	// where replay detection happens elsewhere may pass nil, but server
	// and resource verification must always supply one.
	Replay ReplayChecker
}

VerifyRequest describes one DPoP proof to verify.

Jump to

Keyboard shortcuts

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