keyfunc

package module
v0.3.2 Latest Latest
Warning

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

Go to latest
Published: Mar 27, 2021 License: Apache-2.0 Imports: 15 Imported by: 0

README

Go Report Card Go Reference

keyfunc

The sole purpose of this package is to provide a jwt.KeyFunc for the github.com/dgrijalva/jwt-go package using a JSON Web Key Set (JWKS) for parsing JSON Web Tokens (JWTs).

It's common for an identity provider, such as Keycloak to expose a JWKS via an HTTPS endpoint. This package has the ability to consume that JWKS and produce a jwt.KeyFunc. It is important that a JWKS endpoint is using HTTPS to ensure the keys are from the correct trusted source.

There are no dependencies other than github.com/dgrijalva/jwt-go for this repository.

Supported Algorithms

It is recommended to only use this package for asymmetric signing keys, If you are using HMAC signing keys, this Go package may be unnecessary as the algorithm is symmetric, meaning the key is pre-shared. In this case a JWKS is likely not be the best solution.

Currently, this package supports JWTs signed with an alg that matches one of the following:

  • ES256
  • ES384
  • ES512
  • PS256
  • PS384
  • PS512
  • RS256
  • RS384
  • RS512

Additionally, the supported elliptical curve types are below:

  • P-256
  • P-384
  • P-521

If there are cryptographic algorithms, curve types, or something else already standardized that you'd like supported in this Go package, please open an issue or pull request.

Basic usage

Please also see the examples directory.

import "github.com/MicahParks/keyfunc"
Step 1: Acquire the JWKS URL (optional)

A JWKS URL is not required, one can be created directly from JSON with the keyfunc.New function.

// Get the JWKS URL from an environment variable.
jwksURL := os.Getenv("JWKS_URL")

// Confirm the environment variable is not empty.
if jwksURL == "" {
	log.Fatalln("JWKS_URL environment variable must be populated.")
}
Step 2: Get the JWKS via HTTP
// Create the JWKS from the resource at the given URL.
jwks, err := keyfunc.Get(jwksURL)
if err != nil {
	log.Fatalf("Failed to get the JWKS from the given URL.\nError: %s", err.Error())
}

Additional options can be passed to the keyfunc.Get function via variadic arguments. See keyfunc.Options.

Step 3: Use the keyfunc.JWKS 's JWKS.KeyFunc method as the jwt.KeyFunc when parsing tokens
// Parse the JWT.
token, err := jwt.Parse(jwtB64, jwks.KeyFunc)
if err != nil {
	return nil, fmt.Errorf("failed to parse token: %w", err)
}

The JWKS.KeyFunc method will automatically select the key with the matching kid (if present) and return its public key as the correct Go type to its caller.

Test coverage

Test coverage is currently at 84.3%.

This is with current and expired JWTs, but the hard coded ones are now expired. Using non-expired JWTs would require signing JWTs during testing and would allow for additional error checking. But a bit overkill since I've already done that error checking when the JWTs were valid with no changes. A PR for this that does not introduce any dependencies is welcome though.

Additional features

  • A background refresh of the JWKS keys can be performed. This is possible by passing keyfunc.Options via a variadic argument to the keyfunc.Get function.
    • A custom background refresh interval can be specified.
    • A custom background refresh request context timeout can be specified. Defaults to one minute.
    • A custom background refresh error handling function can be specified. If none is specified, errors go unhandled silently.
  • JWTs with a previously unseen kid can prompt an automatic refresh of the remote JWKS resource.
  • A custom HTTP client can be used. This is possible by passing keyfunc.Options via a variadic argument to the keyfunc.Get function.

TODO

  • Add HMAC support?

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (

	// ErrKIDNotFound indicates that the given key ID was not found in the JWKS.
	ErrKIDNotFound = errors.New("the given key ID was not found in the JWKS")

	// ErrMissingAssets indicates there are required assets missing to create a public key.
	ErrMissingAssets = errors.New("required assets are missing to create a public key")
)
View Source
var (

	// ErrKID indicates that the JWT had an invalid kid.
	ErrKID = errors.New("the JWT has an invalid kid")

	// ErrUnsupportedKeyType indicates the JWT key type is an unsupported type.
	ErrUnsupportedKeyType = errors.New("the JWT key type is unsupported")
)

Functions

This section is empty.

Types

type ErrorHandler

type ErrorHandler func(err error)

ErrorHandler is a function signature that consumes an error.

type JSONKey

type JSONKey struct {
	Curve    string `json:"crv"`
	Exponent string `json:"e"`
	ID       string `json:"kid"`
	Modulus  string `json:"n"`
	X        string `json:"x"`
	Y        string `json:"y"`
	// contains filtered or unexported fields
}

JSONKey represents a raw key inside a JWKS.

func (*JSONKey) ECDSA

func (j *JSONKey) ECDSA() (publicKey *ecdsa.PublicKey, err error)

ECDSA parses a JSONKey and turns it into an ECDSA public key.

func (*JSONKey) RSA

func (j *JSONKey) RSA() (publicKey *rsa.PublicKey, err error)

RSA parses a JSONKey and turns it into an RSA public key.

type JWKS

type JWKS struct {
	Keys map[string]*JSONKey
	// contains filtered or unexported fields
}

JWKS represents a JSON Web Key Set.

func Get

func Get(jwksURL string, options ...Options) (jwks *JWKS, err error)

Get loads the JWKS at the given URL.

func New

func New(jwksBytes json.RawMessage) (jwks *JWKS, err error)

New creates a new JWKS from a raw JSON message.

func (*JWKS) EndBackground

func (j *JWKS) EndBackground()

EndBackground ends the background goroutine to update the JWKs. It can only happen once and is only effective if the JWKS has a background goroutine refreshing the JWKS keys.

func (*JWKS) KeyFunc

func (j *JWKS) KeyFunc(token *jwt.Token) (interface{}, error)

KeyFunc is a compatibility function that matches the signature of github.com/dgrijalva/jwt-go's KeyFunc function.

type Options

type Options struct {

	// Client is the HTTP client used to get the JWKS via HTTP.
	Client *http.Client

	// RefreshInterval is the duration to refresh the JWKS in the background via a new HTTP request. If this is not nil,
	// then a background refresh will be performed in a separate goroutine until the JWKS method EndBackground is
	// called.
	RefreshInterval *time.Duration

	// RefreshTimeout is the duration for the context used to create the HTTP request for a refresh of the JWKS. This
	// defaults to one minute. This is only effectual if RefreshInterval is not nil.
	RefreshTimeout *time.Duration

	// RefreshErrorHandler is a function that consumes errors that happen during a JWKS refresh. This is only effectual
	// if RefreshInterval is not nil.
	RefreshErrorHandler ErrorHandler

	// RefreshUnknownKID indicates that the JWKS should be refreshed via HTTP every time a kid that isn't know is found.
	// This means the
	RefreshUnknownKID *bool
}

Options represents the configuration options for a JWKS.

Directories

Path Synopsis
examples
get

Jump to

Keyboard shortcuts

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