Package jwks

import "funnyhaha.technology/httpkit/jwks"
View the source code

Overview

Package jwks verifies JWT access tokens (RFC 9068) with the keys an authorization server publishes as a JSON Web Key Set, using go-jose. It builds a bearer.VerifyTokenOpFunc, so a resource server protects itself with:

v := jwks.New(jwksURL, issuer, rfc8707.MustResource(resource)).Verifier()
_ = v.Warm(ctx) // Optional: fetch the keys before the first request.
protect := bearer.New(v.Op(), challenge).WithScope("read").Middleware()

The key set is cached for as long as its Cache-Control allows, within bounds, and fetched again early when a token names a key it doesn't have, so a rotated key is picked up straight away. Fetches are limited to one per interval, so tokens naming made-up keys can't make it hammer the authorization server, and while a fetch fails the last good keys are kept.

It's in its own package so that only resource servers depend on go-jose.

Index

Constants

const (
	// DefaultLeeway is the clock skew allowed when checking a token's
	// times. RFC 9068, Section 4, says it's "usually no more than a few
	// minutes".
	DefaultLeeway = 30 * time.Second

	// DefaultRefetchInterval is the least time between fetches of the key
	// set.
	DefaultRefetchInterval = 30 * time.Second

	// DefaultCacheFloor is the shortest time a key set is cached, used too
	// when its response has no max-age.
	DefaultCacheFloor = 5 * time.Minute

	// DefaultCacheCeiling is the longest time a key set is cached.
	DefaultCacheCeiling = 24 * time.Hour

	// DefaultTimeout is how long fetching the key set may take.
	DefaultTimeout = 10 * time.Second
)
source

Defaults, used unless a With method changes them.

Variables

var (
	// ErrKeySet is returned, wrapped in [bearer.ErrUnavailable], when the
	// key set can't be fetched or read.
	ErrKeySet = errors.New("key set unavailable")
)
source

Types

Builder source

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

Builder configures a Verifier. Its With methods return a modified copy.

New source

func New(jwksURL, issuer string, audience rfc8707.Resource) Builder

New returns a builder for a verifier that accepts tokens signed with the keys at jwksURL, issued by issuer, for audience. It panics if jwksURL or issuer is empty.

(Builder) Verifier source

func (b Builder) Verifier() *Verifier

Verifier returns the verifier. It fetches nothing until it's first needed, or Verifier.Warm is called.

(Builder) WithAlgorithms source

func (b Builder) WithAlgorithms(alg jose.SignatureAlgorithm, more ...jose.SignatureAlgorithm) Builder

WithAlgorithms sets the signature algorithms accepted. The default is RS256 only, which RFC 9068, Section 2.1, requires every resource server to support. "none" is never accepted.

(Builder) WithCacheBounds source

func (b Builder) WithCacheBounds(minAge, maxAge time.Duration) Builder

WithCacheBounds sets the shortest and longest time a key set is cached, whatever its Cache-Control says.

(Builder) WithClient source

func (b Builder) WithClient(c *http.Client) Builder

WithClient sets the HTTP client that fetches the key set. The default times out after 10 seconds and doesn't follow redirects. Give it a timeout: a fetch isn't cancelled when the request that started it is, since other requests may be waiting for it.

(Builder) WithLeeway source

func (b Builder) WithLeeway(d time.Duration) Builder

WithLeeway sets the clock skew allowed when checking a token's times.

(Builder) WithNow source

func (b Builder) WithNow(now func() time.Time) Builder

WithNow sets the clock.

(Builder) WithRefetchInterval source

func (b Builder) WithRefetchInterval(d time.Duration) Builder

WithRefetchInterval sets the least time between fetches of the key set.

Verifier source

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

Verifier verifies tokens with a cached key set. It's safe for concurrent use. Build one with New.

(Verifier) Op source

func (v *Verifier) Op() bearer.VerifyTokenOpFunc

Op returns the operation that verifies a token. A token is accepted when it's a JWS signed with one of the accepted algorithms by the key its kid names, its typ is at+jwt, and its claims pass rfc9068.Claims.Validate (RFC 9068, Section 4). Otherwise the error wraps bearer.ErrInvalidToken, unless the key set has never been fetched, when it wraps bearer.ErrUnavailable.

(Verifier) Warm source

func (v *Verifier) Warm(ctx context.Context) error

Warm fetches the key set, so the first request doesn't wait for it. An error isn't fatal: the verifier tries again when it needs the keys.