dpop

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Mar 8, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package dpop implements Demonstrating Proof of Possession (DPoP) per RFC 9449. DPoP binds access tokens to cryptographic key pairs held by clients, preventing stolen tokens from being used without the private key.

Index

Constants

View Source
const (
	// ContextKeyVerificationResult is the context key for the verification result.
	ContextKeyVerificationResult contextKey = "dpop_verification_result"
	// ContextKeyThumbprint is the context key for the JWK thumbprint.
	ContextKeyThumbprint contextKey = "dpop_thumbprint"
)
View Source
const (
	// HeaderDPoP is the header name for DPoP proofs.
	HeaderDPoP = "DPoP"
	// HeaderAuthorization is the standard Authorization header.
	HeaderAuthorization = "Authorization"
	// AuthSchemeBearer is the Bearer authorization scheme.
	AuthSchemeBearer = "Bearer"
	// AuthSchemeDPoP is the DPoP authorization scheme.
	AuthSchemeDPoP = "DPoP"
)

Header names for DPoP.

View Source
const (
	// DPoPTokenType is the required typ header value for DPoP proofs.
	DPoPTokenType = "dpop+jwt"
)

Variables

View Source
var (
	// ErrInvalidKey is returned when a key is malformed or invalid.
	ErrInvalidKey = errors.New("invalid key")
	// ErrUnsupportedAlgorithm is returned for unsupported cryptographic algorithms.
	ErrUnsupportedAlgorithm = errors.New("unsupported algorithm")
)
View Source
var (
	// ErrInvalidProof is returned when a DPoP proof is malformed or invalid.
	ErrInvalidProof = errors.New("invalid DPoP proof")
	// ErrMethodMismatch is returned when the htm claim doesn't match the request method.
	ErrMethodMismatch = errors.New("HTTP method mismatch")
	// ErrURIMismatch is returned when the htu claim doesn't match the request URI.
	ErrURIMismatch = errors.New("HTTP URI mismatch")
	// ErrTokenHashMismatch is returned when the ath claim doesn't match the access token hash.
	ErrTokenHashMismatch = errors.New("access token hash mismatch")
	// ErrProofExpired is returned when a DPoP proof's iat is too old.
	ErrProofExpired = errors.New("DPoP proof expired")
	// ErrNonceMismatch is returned when the nonce claim doesn't match the expected nonce.
	ErrNonceMismatch = errors.New("nonce mismatch")
)
View Source
var ValidHTTPMethods = map[string]bool{
	"GET":     true,
	"HEAD":    true,
	"POST":    true,
	"PUT":     true,
	"DELETE":  true,
	"CONNECT": true,
	"OPTIONS": true,
	"TRACE":   true,
	"PATCH":   true,
}

ValidHTTPMethods contains the valid HTTP methods for DPoP proofs.

Functions

func ComputeAccessTokenHash

func ComputeAccessTokenHash(accessToken string) string

ComputeAccessTokenHash computes the base64url-encoded SHA-256 hash of an access token. This is used for the ath (access token hash) claim in DPoP proofs.

func ComputeThumbprint

func ComputeThumbprint(publicKey *ecdsa.PublicKey) (string, error)

ComputeThumbprint computes the JWK thumbprint per RFC 7638 for an ECDSA public key. For EC keys, the required members are: crv, kty, x, y (in lexicographic order).

func CreateProof

func CreateProof(kp *KeyPair, method, uri string) (string, error)

CreateProof creates a DPoP proof JWT for the given request. The proof is signed with the key pair's private key and embeds the public key in the header.

func CreateProofWithOptions

func CreateProofWithOptions(kp *KeyPair, method, uri string, opts ProofOptions) (string, error)

CreateProofWithOptions creates a DPoP proof JWT with optional parameters.

func GetThumbprint

func GetThumbprint(ctx context.Context) string

GetThumbprint retrieves the DPoP thumbprint from the request context.

func IsValidHTTPMethod

func IsValidHTTPMethod(method string) bool

IsValidHTTPMethod checks if the given method is a valid HTTP method.

func Middleware

func Middleware(config MiddlewareConfig) func(http.Handler) http.Handler

Middleware creates HTTP middleware that validates DPoP proofs.

func OptionalDPoP

func OptionalDPoP(verifier *Verifier) func(http.Handler) http.Handler

OptionalDPoP is a helper function to create middleware that accepts but doesn't require DPoP.

func RequireDPoP

func RequireDPoP(verifier *Verifier) func(http.Handler) http.Handler

RequireDPoP is a helper function to create middleware that requires DPoP.

func VerifyTokenBinding

func VerifyTokenBinding(tokenThumbprint string, proofThumbprint string) error

VerifyTokenBinding verifies that an access token's cnf.jkt claim matches the thumbprint from a verified DPoP proof.

Types

type JWK

type JWK struct {
	Kty string `json:"kty"`           // Key type (EC)
	Crv string `json:"crv"`           // Curve (P-256)
	X   string `json:"x"`             // X coordinate
	Y   string `json:"y"`             // Y coordinate
	Alg string `json:"alg,omitempty"` // Algorithm (ES256)
}

JWK represents a JSON Web Key for embedding in DPoP proof headers.

func ToJWK

func ToJWK(publicKey *ecdsa.PublicKey) (*JWK, error)

ToJWK converts an ECDSA public key to a JWK representation.

func (*JWK) Thumbprint

func (j *JWK) Thumbprint() (string, error)

Thumbprint computes and returns the JWK thumbprint for this key.

func (*JWK) ToPublicKey

func (j *JWK) ToPublicKey() (*ecdsa.PublicKey, error)

ToPublicKey converts a JWK back to an ECDSA public key.

type KeyPair

type KeyPair struct {
	// PrivateKey is the ECDSA private key for signing proofs.
	PrivateKey *ecdsa.PrivateKey
	// Thumbprint is the JWK thumbprint (RFC 7638) of the public key.
	Thumbprint string
}

KeyPair represents an ES256 key pair for DPoP.

func DeserializeKeyPair

func DeserializeKeyPair(s *SerializedKeyPair) (*KeyPair, error)

DeserializeKeyPair reconstructs a key pair from its serialized form.

func DeserializeKeyPairJSON

func DeserializeKeyPairJSON(data []byte) (*KeyPair, error)

DeserializeKeyPairJSON reconstructs a key pair from JSON bytes.

func GenerateKeyPair

func GenerateKeyPair() (*KeyPair, error)

GenerateKeyPair creates a new ES256 (P-256/secp256r1) key pair for DPoP. The thumbprint is computed per RFC 7638 using SHA-256.

func (*KeyPair) PublicKey

func (kp *KeyPair) PublicKey() *ecdsa.PublicKey

PublicKey returns the public key portion of the key pair.

func (*KeyPair) Serialize

func (kp *KeyPair) Serialize() (*SerializedKeyPair, error)

Serialize converts the key pair to a serializable format.

func (*KeyPair) SerializeJSON

func (kp *KeyPair) SerializeJSON() ([]byte, error)

SerializeJSON serializes the key pair to JSON bytes.

func (*KeyPair) Signer

func (kp *KeyPair) Signer() crypto.Signer

Signer returns a crypto.Signer interface for the private key.

type MiddlewareConfig

type MiddlewareConfig struct {
	// Verifier is the DPoP verifier to use.
	Verifier *Verifier
	// RequireDPoP when true rejects requests without DPoP proofs.
	// When false, requests without DPoP proofs are allowed through.
	RequireDPoP bool
	// ExtractAccessToken is a function to extract the access token from the request.
	// If nil, the middleware extracts from the Authorization header.
	ExtractAccessToken func(r *http.Request) string
	// OnError is called when verification fails.
	// If nil, a 401 Unauthorized response is sent.
	OnError func(w http.ResponseWriter, r *http.Request, err error)
}

MiddlewareConfig contains configuration for the DPoP middleware.

type ParsedProof

type ParsedProof struct {
	// Claims contains the proof claims.
	Claims *ProofClaims
	// PublicKey is the public key extracted from the jwk header.
	PublicKey *ecdsa.PublicKey
	// Thumbprint is the JWK thumbprint of the public key.
	Thumbprint string
}

ParsedProof contains the parsed and validated DPoP proof.

func ParseProof

func ParseProof(proofString string) (*ParsedProof, error)

ParseProof parses a DPoP proof JWT without verifying it against request parameters. Use this for initial parsing before validation.

type ProofClaims

type ProofClaims struct {
	jwt.RegisteredClaims

	// HTTPMethod is the HTTP method of the request (htm claim).
	// REQUIRED. The value of the HTTP method of the request to which the JWT is attached.
	HTTPMethod string `json:"htm"`

	// HTTPURI is the HTTP URI of the request (htu claim).
	// REQUIRED. The HTTP target URI, without query and fragment parts.
	HTTPURI string `json:"htu"`

	// AccessTokenHash is the base64url-encoded SHA-256 hash of the access token (ath claim).
	// REQUIRED when the DPoP proof is sent with a request for a protected resource.
	AccessTokenHash string `json:"ath,omitempty"`

	// Nonce is the server-provided nonce value (nonce claim).
	// Used for replay protection when the server issues nonces.
	Nonce string `json:"nonce,omitempty"`
}

ProofClaims represents the claims for a DPoP proof JWT per RFC 9449.

func NewProofClaims

func NewProofClaims(method, uri string, accessToken string) *ProofClaims

NewProofClaims creates a new DPoP proof claims structure. The method and uri parameters are required. The accessToken parameter is optional - if provided, the ath claim will be computed.

func (*ProofClaims) WithAccessToken

func (c *ProofClaims) WithAccessToken(accessToken string) *ProofClaims

WithAccessToken computes and sets the ath claim from an access token.

func (*ProofClaims) WithNonce

func (c *ProofClaims) WithNonce(nonce string) *ProofClaims

WithNonce adds a server-provided nonce to the claims.

type ProofHeader

type ProofHeader struct {
	Algorithm string `json:"alg"` // Algorithm (ES256, ES384, ES512)
	Type      string `json:"typ"` // MUST be "dpop+jwt"
	JWK       *JWK   `json:"jwk"` // Public key
}

ProofHeader represents the JWT header for a DPoP proof. The header MUST include the public key (jwk) and MUST have typ=dpop+jwt.

type ProofOptions

type ProofOptions struct {
	// AccessToken is the access token to bind to the proof (for ath claim).
	AccessToken string //nolint:gosec // G117: field holds runtime token value
	// Nonce is a server-provided nonce for replay protection.
	Nonce string
}

ProofOptions contains optional parameters for creating a DPoP proof.

type SerializedKeyPair

type SerializedKeyPair struct {
	PrivateKeyD string `json:"d"`          // Private key scalar
	PublicKeyX  string `json:"x"`          // Public key X coordinate
	PublicKeyY  string `json:"y"`          // Public key Y coordinate
	Curve       string `json:"crv"`        // Curve name
	Thumbprint  string `json:"thumbprint"` // JWK thumbprint
}

Serialize serializes the key pair for storage. The private key is encoded in PKCS#8 format and base64url encoded.

type VerificationConfig

type VerificationConfig struct {
	// MaxAge is the maximum age of a DPoP proof (based on iat claim).
	// Default: 5 minutes.
	MaxAge time.Duration

	// AllowedClockSkew is the maximum clock skew to allow when validating iat.
	// Default: 30 seconds.
	AllowedClockSkew time.Duration

	// RequireAccessTokenBinding when true requires the ath claim to be present
	// and match the provided access token hash.
	RequireAccessTokenBinding bool

	// NonceValidator is an optional function to validate server-provided nonces.
	// If set and returns an error, verification fails with ErrNonceMismatch.
	NonceValidator func(ctx context.Context, nonce string) error
}

VerificationConfig contains configuration for DPoP proof verification.

func DefaultVerificationConfig

func DefaultVerificationConfig() VerificationConfig

DefaultVerificationConfig returns the default verification configuration.

type VerificationRequest

type VerificationRequest struct {
	// Proof is the DPoP proof JWT string.
	Proof string
	// Method is the HTTP method of the request (e.g., "POST").
	Method string
	// URI is the HTTP URI of the request (scheme + host + path, no query or fragment).
	URI string
	// AccessToken is the access token to verify binding against (optional).
	// Required if VerificationConfig.RequireAccessTokenBinding is true.
	AccessToken string //nolint:gosec // G117: field holds runtime token value
	// ExpectedNonce is the expected server-provided nonce (optional).
	ExpectedNonce string
}

VerificationRequest contains the parameters for verifying a DPoP proof.

type VerificationResult

type VerificationResult struct {
	// Thumbprint is the JWK thumbprint of the public key used to sign the proof.
	Thumbprint string
	// JTI is the unique identifier of the proof.
	JTI string
	// IssuedAt is when the proof was created.
	IssuedAt time.Time
}

VerificationResult contains the result of a successful verification.

func GetVerificationResult

func GetVerificationResult(ctx context.Context) *VerificationResult

GetVerificationResult retrieves the DPoP verification result from the request context.

type Verifier

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

Verifier verifies DPoP proofs.

func NewVerifier

func NewVerifier(config VerificationConfig) *Verifier

NewVerifier creates a new DPoP verifier with the given configuration.

func (*Verifier) Verify

Verify verifies a DPoP proof against the given request parameters.

Jump to

Keyboard shortcuts

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