jwk

package
v2.0.21 Latest Latest
Warning

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

Go to latest
Published: Mar 7, 2024 License: MIT Imports: 37 Imported by: 471

README

JWK Go Reference

Package jwk implements JWK as described in RFC7517. If you are looking to use JWT wit JWKs, look no further than github.com/lestrrat-go/jwx.

  • Parse and work with RSA/EC/Symmetric/OKP JWK types
    • Convert to and from JSON
    • Convert to and from raw key types (e.g. *rsa.PrivateKey)
  • Ability to keep a JWKS fresh using *jwk.AutoRefersh

Supported key types:

kty Curve Go Key Type
RSA N/A rsa.PrivateKey / rsa.PublicKey (2)
EC P-256
P-384
P-521
secp256k1 (1)
ecdsa.PrivateKey / ecdsa.PublicKey (2)
oct N/A []byte
OKP Ed25519 (1) ed25519.PrivateKey / ed25519.PublicKey (2)
X25519 (1) (jwx/)x25519.PrivateKey / x25519.PublicKey (2)
  • Note 1: Experimental
  • Note 2: Either value or pointers accepted (e.g. rsa.PrivateKey or *rsa.PrivateKey)

Documentation

Please read the API reference, or the how-to style documentation on how to use JWK can be found in the docs directory.

Auto-Refresh a key during a long running process

package examples_test

import (
  "context"
  "fmt"
  "time"

  "github.com/lestrrat-go/jwx/v2/jwk"
)

func ExampleJWK_Cache() {
  ctx, cancel := context.WithCancel(context.Background())

  const googleCerts = `https://www.googleapis.com/oauth2/v3/certs`

  // First, set up the `jwk.Cache` object. You need to pass it a
  // `context.Context` object to control the lifecycle of the background fetching goroutine.
  //
  // Note that by default refreshes only happen very 15 minutes at the
  // earliest. If you need to control this, use `jwk.WithRefreshWindow()`
  c := jwk.NewCache(ctx)

  // Tell *jwk.Cache that we only want to refresh this JWKS
  // when it needs to (based on Cache-Control or Expires header from
  // the HTTP response). If the calculated minimum refresh interval is less
  // than 15 minutes, don't go refreshing any earlier than 15 minutes.
  c.Register(googleCerts, jwk.WithMinRefreshInterval(15*time.Minute))

  // Refresh the JWKS once before getting into the main loop.
  // This allows you to check if the JWKS is available before we start
  // a long-running program
  _, err := c.Refresh(ctx, googleCerts)
  if err != nil {
    fmt.Printf("failed to refresh google JWKS: %s\n", err)
    return
  }

  // Pretend that this is your program's main loop
MAIN:
  for {
    select {
    case <-ctx.Done():
      break MAIN
    default:
    }
    keyset, err := c.Get(ctx, googleCerts)
    if err != nil {
      fmt.Printf("failed to fetch google JWKS: %s\n", err)
      return
    }
    _ = keyset
    // The returned `keyset` will always be "reasonably" new.
    //
    // By "reasonably" we mean that we cannot guarantee that the keys will be refreshed
    // immediately after it has been rotated in the remote source. But it should be close\
    // enough, and should you need to forcefully refresh the token using the `(jwk.Cache).Refresh()` method.
    //
    // If re-fetching the keyset fails, a cached version will be returned from the previous successful
    // fetch upon calling `(jwk.Cache).Fetch()`.

    // Do interesting stuff with the keyset... but here, we just
    // sleep for a bit
    time.Sleep(time.Second)

    // Because we're a dummy program, we just cancel the loop now.
    // If this were a real program, you prosumably loop forever
    cancel()
  }
  // OUTPUT:
}

source: examples/jwk_cache_example_test.go

Parse and use a JWK key:

package examples_test

import (
  "context"
  "fmt"
  "log"

  "github.com/lestrrat-go/jwx/v2/internal/json"
  "github.com/lestrrat-go/jwx/v2/jwk"
)

func ExampleJWK_Usage() {
  // Use jwk.Cache if you intend to keep reuse the JWKS over and over
  set, err := jwk.Fetch(context.Background(), "https://www.googleapis.com/oauth2/v3/certs")
  if err != nil {
    log.Printf("failed to parse JWK: %s", err)
    return
  }

  // Key sets can be serialized back to JSON
  {
    jsonbuf, err := json.Marshal(set)
    if err != nil {
      log.Printf("failed to marshal key set into JSON: %s", err)
      return
    }
    log.Printf("%s", jsonbuf)
  }

  for it := set.Keys(context.Background()); it.Next(context.Background()); {
    pair := it.Pair()
    key := pair.Value.(jwk.Key)

    var rawkey interface{} // This is the raw key, like *rsa.PrivateKey or *ecdsa.PrivateKey
    if err := key.Raw(&rawkey); err != nil {
      log.Printf("failed to create public key: %s", err)
      return
    }
    // Use rawkey for jws.Verify() or whatever.
    _ = rawkey

    // You can create jwk.Key from a raw key, too
    fromRawKey, err := jwk.FromRaw(rawkey)
    if err != nil {
      log.Printf("failed to acquire raw key from jwk.Key: %s", err)
      return
    }

    // Keys can be serialized back to JSON
    jsonbuf, err := json.Marshal(key)
    if err != nil {
      log.Printf("failed to marshal key into JSON: %s", err)
      return
    }

    fromJSONKey, err := jwk.Parse(jsonbuf)
    if err != nil {
      log.Printf("failed to parse json: %s", err)
      return
    }
    _ = fromJSONKey
    _ = fromRawKey
  }
  // OUTPUT:
}

//nolint:govet
func ExampleJWK_MarshalJSON() {
  // JWKs that inherently involve randomness such as RSA and EC keys are
  // not used in this example, because they may produce different results
  // depending on the environment.
  //
  // (In fact, even if you use a static source of randomness, tests may fail
  // because of internal changes in the Go runtime).

  raw := []byte("01234567890123456789012345678901234567890123456789ABCDEF")

  // This would create a symmetric key
  key, err := jwk.FromRaw(raw)
  if err != nil {
    fmt.Printf("failed to create symmetric key: %s\n", err)
    return
  }
  if _, ok := key.(jwk.SymmetricKey); !ok {
    fmt.Printf("expected jwk.SymmetricKey, got %T\n", key)
    return
  }

  key.Set(jwk.KeyIDKey, "mykey")

  buf, err := json.MarshalIndent(key, "", "  ")
  if err != nil {
    fmt.Printf("failed to marshal key into JSON: %s\n", err)
    return
  }
  fmt.Printf("%s\n", buf)

  // OUTPUT:
  // {
  //   "k": "MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODlBQkNERUY",
  //   "kid": "mykey",
  //   "kty": "oct"
  // }
}

source: examples/jwk_example_test.go

Documentation

Overview

Package jwk implements JWK as described in https://tools.ietf.org/html/rfc7517

Index

Constants

View Source
const (
	ECDSACrvKey = "crv"
	ECDSADKey   = "d"
	ECDSAXKey   = "x"
	ECDSAYKey   = "y"
)
View Source
const (
	KeyTypeKey                = "kty"
	KeyUsageKey               = "use"
	KeyOpsKey                 = "key_ops"
	AlgorithmKey              = "alg"
	KeyIDKey                  = "kid"
	X509URLKey                = "x5u"
	X509CertChainKey          = "x5c"
	X509CertThumbprintKey     = "x5t"
	X509CertThumbprintS256Key = "x5t#S256"
)
View Source
const (
	OKPCrvKey = "crv"
	OKPDKey   = "d"
	OKPXKey   = "x"
)
View Source
const (
	RSADKey  = "d"
	RSADPKey = "dp"
	RSADQKey = "dq"
	RSAEKey  = "e"
	RSANKey  = "n"
	RSAPKey  = "p"
	RSAQIKey = "qi"
	RSAQKey  = "q"
)
View Source
const (
	SymmetricOctetsKey = "k"
)

Variables

This section is empty.

Functions

func AssignKeyID

func AssignKeyID(key Key, options ...AssignKeyIDOption) error

AssignKeyID is a convenience function to automatically assign the "kid" section of the key, if it already doesn't have one. It uses Key.Thumbprint method with crypto.SHA256 as the default hashing algorithm

func AvailableCurves

func AvailableCurves() []elliptic.Curve

func CurveForAlgorithm

func CurveForAlgorithm(alg jwa.EllipticCurveAlgorithm) (elliptic.Curve, bool)

func DecodePEM

func DecodePEM(src []byte) (interface{}, []byte, error)

DecodePEM decodes a key in PEM encoded ASN.1 DER format. and returns a raw key

func EncodePEM

func EncodePEM(v interface{}) ([]byte, error)

EncodePEM encodes the key into a PEM encoded ASN.1 DER format. The key can be a jwk.Key or a raw key instance, but it must be one of the types supported by `x509` package.

Internally, it uses the same routine as `jwk.EncodeX509()`, and therefore the same caveats apply

func EncodeX509

func EncodeX509(v interface{}) (string, []byte, error)

EncodeX509 encodes the key into a byte sequence in ASN.1 DER format suitable for to be PEM encoded. The key can be a jwk.Key or a raw key instance, but it must be one of the types supported by `x509` package.

This function will try to do the right thing depending on the key type (i.e. switch between `x509.MarshalPKCS1PRivateKey` and `x509.MarshalECPrivateKey`), but for public keys, it will always use `x509.MarshalPKIXPublicKey`. Please manually perform the encoding if you need more fine grained control

The first return value is the name that can be used for `(pem.Block).Type`. The second return value is the encoded byte sequence.

func Equal added in v2.0.13

func Equal(k1, k2 Key) bool

Equal compares two keys and returns true if they are equal. The comparison is solely done against the thumbprints of k1 and k2. It is possible for keys that have, for example, different key IDs, key usage, etc, to be considered equal.

func IsKeyValidationError added in v2.0.16

func IsKeyValidationError(err error) bool

func IsPrivateKey added in v2.0.14

func IsPrivateKey(k Key) (bool, error)

IsPrivateKey returns true if the supplied key is a private key of an asymmetric key pair. The argument `k` must implement the `AsymmetricKey` interface.

An error is returned if the supplied key is not an `AsymmetricKey`.

func NewKeyValidationError added in v2.0.16

func NewKeyValidationError(err error) error

NewKeyValidationError wraps the given error with an error that denotes `key.Validate()` has failed. This error type should ONLY be used as return value from the `Validate()` method.

func ParseRawKey

func ParseRawKey(data []byte, rawkey interface{}) error

ParseRawKey is a combination of ParseKey and Raw. It parses a single JWK key, and assigns the "raw" key to the given parameter. The key must either be a pointer to an empty interface, or a pointer to the actual raw key type such as *rsa.PrivateKey, *ecdsa.PublicKey, *[]byte, etc.

func Pem

func Pem(v interface{}) ([]byte, error)

Pem serializes the given jwk.Key in PEM encoded ASN.1 DER format, using either PKCS8 for private keys and PKIX for public keys. If you need to encode using PKCS1 or SEC1, you must do it yourself.

Argument must be of type jwk.Key or jwk.Set

Currently only EC (including Ed25519) and RSA keys (and jwk.Set comprised of these key types) are supported.

func PublicRawKeyOf

func PublicRawKeyOf(v interface{}) (interface{}, error)

PublicRawKeyOf returns the corresponding public key of the given value `v` (e.g. given *rsa.PrivateKey, *rsa.PublicKey is returned) If `v` is already a public key, the key itself is returned.

The returned value will always be a pointer to the public key, except when a []byte (e.g. symmetric key, ed25519 key) is passed to `v`. In this case, the same []byte value is returned.

func RegisterCustomField

func RegisterCustomField(name string, object interface{})

RegisterCustomField allows users to specify that a private field be decoded as an instance of the specified type. This option has a global effect.

For example, suppose you have a custom field `x-birthday`, which you want to represent as a string formatted in RFC3339 in JSON, but want it back as `time.Time`.

In that case you would register a custom field as follows

jwk.RegisterCustomField(`x-birthday`, timeT)

Then `key.Get("x-birthday")` will still return an `interface{}`, but you can convert its type to `time.Time`

bdayif, _ := key.Get(`x-birthday`)
bday := bdayif.(time.Time)

func SetGlobalFetcher added in v2.0.10

func SetGlobalFetcher(f httprc.Fetcher)

SetGlobalFetcher allows users to specify a custom global fetcher, which is used by the `Fetch` function. Assigning `nil` forces the default fetcher to be (re)created when the next call to `jwk.Fetch` occurs

You only need to call this function when you want to either change the fetching behavior (for example, you want to change how the default whitelist is handled), or when you want to control the lifetime of the global fetcher, for example for tests that require a clean shutdown.

If you do use this function to set a custom fetcher and you control its termination, make sure that you call `jwk.SetGlobalFetcher()` one more time (possibly with `nil`) to assign a valid fetcher. Otherwise, once the fetcher is invalidated, subsequent calls to `jwk.Fetch` may hang, causing very hard to debug problems.

If you are sure you no longer need `jwk.Fetch` after terminating the fetcher, then you the above caution is not necessary.

Types

type AssignKeyIDOption

type AssignKeyIDOption interface {
	Option
	// contains filtered or unexported methods
}

func WithThumbprintHash

func WithThumbprintHash(v crypto.Hash) AssignKeyIDOption

type AsymmetricKey added in v2.0.14

type AsymmetricKey interface {
	IsPrivate() bool
}

AsymmetricKey describes a Key that represents an key in an asymmetric key pair, which in turn can be either a private or a public key. This interface allows those keys to be queried if they are one or the other.

type Cache

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

Cache is a container that keeps track of Set object by their source URLs. The Set objects are stored in memory, and are refreshed automatically behind the scenes.

Before retrieving the Set objects, the user must pre-register the URLs they intend to use by calling `Register()`

c := jwk.NewCache(ctx)
c.Register(url, options...)

Once registered, you can call `Get()` to retrieve the Set object.

All JWKS objects that are retrieved via this mechanism should be treated read-only, as they are shared among all consumers, as well as the `jwk.Cache` object.

There are cases where `jwk.Cache` and `jwk.CachedSet` should and should not be used.

First and foremost, do NOT use a cache for those JWKS objects that need constant checking. For example, unreliable or user-provided JWKS (i.e. those JWKS that are not from a well-known provider) should not be fetched through a `jwk.Cache` or `jwk.CachedSet`.

For example, if you have a flaky JWKS server for development that can go down often, you should consider alternatives such as providing `http.Client` with a caching `http.RoundTripper` configured (see `jwk.WithHTTPClient`), setting up a reverse proxy, etc. These techniques allow you to setup a more robust way to both cache and report precise causes of the problems than using `jwk.Cache` or `jwk.CachedSet`. If you handle the caching at the HTTP level like this, you will be able to use a simple `jwk.Fetch` call and not worry about the cache.

User-provided JWKS objects may also be problematic, as it may go down unexpectedly (and frequently!), and it will be hard to detect when the URLs or its contents are swapped.

A good use-case for `jwk.Cache` and `jwk.CachedSet` are for "stable" JWKS objects.

When we say "stable", we are thinking of JWKS that should mostly be ALWAYS available. A good example are those JWKS objects provided by major cloud providers such as Google Cloud, AWS, or Azure. Stable JWKS may still experience intermittent network connectivity problems, but you can expect that they will eventually recover in relatively short period of time. They rarely change URLs, and the contents are expected to be valid or otherwise it would cause havoc to those providers

We also know that these stable JWKS objects are rotated periodically, which is a perfect use for `jwk.Cache` and `jwk.CachedSet`. The caches can be configured to perodically refresh the JWKS thereby keeping them fresh without extra intervention from the developer.

Notice that for these recommended use-cases the requirement to check the validity or the availability of the JWKS objects are non-existent, as it is expected that they will be available and will be valid. The caching mechanism can hide intermittent connectivity problems as well as keep the objects mostly fresh.

func NewCache

func NewCache(ctx context.Context, options ...CacheOption) *Cache

NewCache creates a new `jwk.Cache` object.

Please refer to the documentation for `httprc.New` for more details.

func (*Cache) Get

func (c *Cache) Get(ctx context.Context, u string) (Set, error)

Get returns the stored JWK set (`Set`) from the cache.

Please refer to the documentation for `(httprc.Cache).Get` for more details.

func (*Cache) IsRegistered

func (c *Cache) IsRegistered(u string) bool

IsRegistered returns true if the given URL `u` has already been registered in the cache.

Please refer to the documentation for `(httprc.Cache).IsRegistered` for more details.

func (*Cache) Refresh

func (c *Cache) Refresh(ctx context.Context, u string) (Set, error)

Refresh is identical to Get(), except it always fetches the specified resource anew, and updates the cached content

Please refer to the documentation for `(httprc.Cache).Refresh` for more details

func (*Cache) Register

func (c *Cache) Register(u string, options ...RegisterOption) error

Register registers a URL to be managed by the cache. URLs must be registered before issuing `Get`

This method is almost identical to `(httprc.Cache).Register`, except it accepts some extra options.

Use `jwk.WithParser` to configure how the JWKS should be parsed, such as passing it extra options.

Please refer to the documentation for `(httprc.Cache).Register` for more details.

Register does not check for the validity of the url being registered. If you need to make sure that a url is valid before entering your main loop, call `Refresh` once to make sure the JWKS is available.

_ = cache.Register(url)
if _, err := cache.Refresh(ctx, url); err != nil {
  // url is not a valid JWKS
  panic(err)
}

func (*Cache) Snapshot

func (c *Cache) Snapshot() *httprc.Snapshot

func (*Cache) Unregister

func (c *Cache) Unregister(u string) error

Unregister removes the given URL `u` from the cache.

Please refer to the documentation for `(httprc.Cache).Unregister` for more details.

type CacheOption

type CacheOption interface {
	Option
	// contains filtered or unexported methods
}

CacheOption is a type of Option that can be passed to the the `jwk.NewCache()` function.

func WithErrSink

func WithErrSink(v ErrSink) CacheOption

WithErrSink specifies the `httprc.ErrSink` object that handles errors that occurred during the cache's execution.

See the documentation in `httprc.WithErrSink` for more details.

func WithRefreshWindow

func WithRefreshWindow(v time.Duration) CacheOption

WithRefreshWindow specifies the interval between checks for refreshes.

See the documentation in `httprc.WithRefreshWindow` for more details.

type CachedSet

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

CachedSet is a thin shim over jwk.Cache that allows the user to cloack jwk.Cache as if it's a `jwk.Set`. Behind the scenes, the `jwk.Set` is retrieved from the `jwk.Cache` for every operation.

Since `jwk.CachedSet` always deals with a cached version of the `jwk.Set`, all operations that mutate the object (such as AddKey(), RemoveKey(), et. al) are no-ops and return an error.

Note that since this is a utility shim over `jwk.Cache`, you _will_ lose the ability to control the finer details (such as controlling how long to wait for in case of a fetch failure using `context.Context`)

Make sure that you read the documentation for `jwk.Cache` as well.

func (*CachedSet) AddKey

func (*CachedSet) AddKey(_ Key) error

Add is a no-op for `jwk.CachedSet`, as the `jwk.Set` should be treated read-only

func (*CachedSet) Clear

func (*CachedSet) Clear() error

Clear is a no-op for `jwk.CachedSet`, as the `jwk.Set` should be treated read-only

func (*CachedSet) Clone

func (cs *CachedSet) Clone() (Set, error)

func (*CachedSet) Get

func (cs *CachedSet) Get(name string) (interface{}, bool)

Get returns the value of non-Key field stored in the jwk.Set

func (*CachedSet) Index

func (cs *CachedSet) Index(key Key) int

func (*CachedSet) Iterate

func (cs *CachedSet) Iterate(ctx context.Context) HeaderIterator

func (*CachedSet) Key

func (cs *CachedSet) Key(idx int) (Key, bool)

Key returns the Key at the specified index

func (*CachedSet) Keys

func (cs *CachedSet) Keys(ctx context.Context) KeyIterator

func (*CachedSet) Len

func (cs *CachedSet) Len() int

func (*CachedSet) LookupKeyID

func (cs *CachedSet) LookupKeyID(kid string) (Key, bool)

func (*CachedSet) Remove

func (*CachedSet) Remove(_ string) error

Remove is a no-op for `jwk.CachedSet`, as the `jwk.Set` should be treated read-only

func (*CachedSet) RemoveKey

func (*CachedSet) RemoveKey(_ Key) error

RemoveKey is a no-op for `jwk.CachedSet`, as the `jwk.Set` should be treated read-only

func (*CachedSet) Set

func (*CachedSet) Set(_ string, _ interface{}) error

Set is a no-op for `jwk.CachedSet`, as the `jwk.Set` should be treated read-only

type DecodeCtx

type DecodeCtx interface {
	json.DecodeCtx
	IgnoreParseError() bool
}

type ECDSAPrivateKey

type ECDSAPrivateKey interface {
	Key
	FromRaw(*ecdsa.PrivateKey) error
	Crv() jwa.EllipticCurveAlgorithm
	D() []byte
	X() []byte
	Y() []byte
}

type ECDSAPublicKey

type ECDSAPublicKey interface {
	Key
	FromRaw(*ecdsa.PublicKey) error
	Crv() jwa.EllipticCurveAlgorithm
	X() []byte
	Y() []byte
}

type ErrSink

type ErrSink = httprc.ErrSink

type FetchFunc

type FetchFunc func(context.Context, string, ...FetchOption) (Set, error)

func (FetchFunc) Fetch

func (f FetchFunc) Fetch(ctx context.Context, u string, options ...FetchOption) (Set, error)

type FetchOption

type FetchOption interface {
	Option
	// contains filtered or unexported methods
}

FetchOption is a type of Option that can be passed to `jwk.Fetch()` FetchOption also implements the `RegisterOption`, and thus can safely be passed to `(*jwk.Cache).Register()`

func WithFetchWhitelist

func WithFetchWhitelist(v Whitelist) FetchOption

WithFetchWhitelist specifies the Whitelist object to use when fetching JWKs from a remote source. This option can be passed to both `jwk.Fetch()`, `jwk.NewCache()`, and `(*jwk.Cache).Configure()`

func WithHTTPClient

func WithHTTPClient(v HTTPClient) FetchOption

WithHTTPClient allows users to specify the "net/http".Client object that is used when fetching jwk.Set objects.

type Fetcher

type Fetcher interface {
	Fetch(context.Context, string, ...FetchOption) (Set, error)
}

type HTTPClient

type HTTPClient = httprc.HTTPClient

type HeaderIterator

type HeaderIterator = mapiter.Iterator

type HeaderPair

type HeaderPair = mapiter.Pair

type HeaderVisitor

type HeaderVisitor = iter.MapVisitor

type HeaderVisitorFunc

type HeaderVisitorFunc = iter.MapVisitorFunc

type InsecureWhitelist

type InsecureWhitelist struct{}

InsecureWhitelist allows any URLs to be fetched. This is the default behavior of `jwk.Fetch()`, but this exists to allow other libraries (such as jws, via jws.VerifyAuto) and users to be able to explicitly state that they intend to not check the URLs that are being fetched

func (InsecureWhitelist) IsAllowed

func (InsecureWhitelist) IsAllowed(string) bool

type Key

type Key interface {
	// Get returns the value of a single field. The second boolean return value
	// will be false if the field is not stored in the source
	//
	// This method, which returns an `interface{}`, exists because
	// these objects can contain extra _arbitrary_ fields that users can
	// specify, and there is no way of knowing what type they could be
	Get(string) (interface{}, bool)

	// Set sets the value of a single field. Note that certain fields,
	// notably "kty", cannot be altered, but will not return an error
	//
	// This method, which takes an `interface{}`, exists because
	// these objects can contain extra _arbitrary_ fields that users can
	// specify, and there is no way of knowing what type they could be
	Set(string, interface{}) error

	// Remove removes the field associated with the specified key.
	// There is no way to remove the `kty` (key type). You will ALWAYS be left with one field in a jwk.Key.
	Remove(string) error
	// Validate performs _minimal_ checks if the data stored in the key are valid.
	// By minimal, we mean that it does not check if the key is valid for use in
	// cryptographic operations. For example, it does not check if an RSA key's
	// `e` field is a valid exponent, or if the `n` field is a valid modulus.
	// Instead, it checks for things such as the _presence_ of some required fields,
	// or if certain keys' values are of particular length.
	//
	// Note that depending on th underlying key type, use of this method requires
	// that multiple fields in the key are properly populated. For example, an EC
	// key's "x", "y" fields cannot be validated unless the "crv" field is populated first.
	//
	// Validate is never called by `UnmarshalJSON()` or `Set`. It must explicitly be
	// called by the user
	Validate() error

	// Raw creates the corresponding raw key. For example,
	// EC types would create *ecdsa.PublicKey or *ecdsa.PrivateKey,
	// and OctetSeq types create a []byte key.
	//
	// If you do not know the exact type of a jwk.Key before attempting
	// to obtain the raw key, you can simply pass a pointer to an
	// empty interface as the first argument.
	//
	// If you already know the exact type, it is recommended that you
	// pass a pointer to the zero value of the actual key type (e.g. &rsa.PrivateKey)
	// for efficiency.
	Raw(interface{}) error

	// Thumbprint returns the JWK thumbprint using the indicated
	// hashing algorithm, according to RFC 7638
	Thumbprint(crypto.Hash) ([]byte, error)

	// Iterate returns an iterator that returns all keys and values.
	// See github.com/lestrrat-go/iter for a description of the iterator.
	Iterate(ctx context.Context) HeaderIterator

	// Walk is a utility tool that allows a visitor to iterate all keys and values
	Walk(context.Context, HeaderVisitor) error

	// AsMap is a utility tool that returns a new map that contains the same fields as the source
	AsMap(context.Context) (map[string]interface{}, error)

	// PrivateParams returns the non-standard elements in the source structure
	// WARNING: DO NOT USE PrivateParams() IF YOU HAVE CONCURRENT CODE ACCESSING THEM.
	// Use `AsMap()` to get a copy of the entire header, or use `Iterate()` instead
	PrivateParams() map[string]interface{}

	// Clone creates a new instance of the same type
	Clone() (Key, error)

	// PublicKey creates the corresponding PublicKey type for this object.
	// All fields are copied onto the new public key, except for those that are not allowed.
	//
	// If the key is already a public key, it returns a new copy minus the disallowed fields as above.
	PublicKey() (Key, error)

	// KeyType returns the `kty` of a JWK
	KeyType() jwa.KeyType
	// KeyUsage returns `use` of a JWK
	KeyUsage() string
	// KeyOps returns `key_ops` of a JWK
	KeyOps() KeyOperationList

	// Algorithm returns the value of the `alg` field
	//
	// This field may contain either `jwk.SignatureAlgorithm` or `jwk.KeyEncryptionAlgorithm`.
	// This is why there exists a `jwa.KeyAlgorithm` type that encompases both types.
	Algorithm() jwa.KeyAlgorithm
	// KeyID returns `kid` of a JWK
	KeyID() string
	// X509URL returns `x5u` of a JWK
	X509URL() string
	// X509CertChain returns `x5c` of a JWK
	X509CertChain() *cert.Chain
	// X509CertThumbprint returns `x5t` of a JWK
	X509CertThumbprint() string
	// X509CertThumbprintS256 returns `x5t#S256` of a JWK
	X509CertThumbprintS256() string
	// contains filtered or unexported methods
}

Key defines the minimal interface for each of the key types. Their use and implementation differ significantly between each key types, so you should use type assertions to perform more specific tasks with each key

func FromRaw

func FromRaw(key interface{}) (Key, error)

FromRaw creates a jwk.Key from the given key (RSA/ECDSA/symmetric keys).

The constructor auto-detects the type of key to be instantiated based on the input type:

  • "crypto/rsa".PrivateKey and "crypto/rsa".PublicKey creates an RSA based key
  • "crypto/ecdsa".PrivateKey and "crypto/ecdsa".PublicKey creates an EC based key
  • "crypto/ed25519".PrivateKey and "crypto/ed25519".PublicKey creates an OKP based key
  • []byte creates a symmetric key

func ParseKey

func ParseKey(data []byte, options ...ParseOption) (Key, error)

ParseKey parses a single key JWK. Unlike `jwk.Parse` this method will report failure if you attempt to pass a JWK set. Only use this function when you know that the data is a single JWK.

Given a WithPEM(true) option, this function assumes that the given input is PEM encoded ASN.1 DER format key.

Note that a successful parsing of any type of key does NOT necessarily guarantee a valid key. For example, no checks against expiration dates are performed for certificate expiration, no checks against missing parameters are performed, etc.

func PublicKeyOf

func PublicKeyOf(v interface{}) (Key, error)

PublicKeyOf returns the corresponding public version of the jwk.Key. If `v` is a SymmetricKey, then the same value is returned. If `v` is already a public key, the key itself is returned.

If `v` is a private key type that has a `PublicKey()` method, be aware that all fields will be copied onto the new public key. It is the caller's responsibility to remove any fields, if necessary

If `v` is a raw key, the key is first converted to a `jwk.Key`

type KeyIterator

type KeyIterator = arrayiter.Iterator

type KeyOperation

type KeyOperation string
const (
	KeyOpSign       KeyOperation = "sign"       // (compute digital signature or MAC)
	KeyOpVerify     KeyOperation = "verify"     // (verify digital signature or MAC)
	KeyOpEncrypt    KeyOperation = "encrypt"    // (encrypt content)
	KeyOpDecrypt    KeyOperation = "decrypt"    // (decrypt content and validate decryption, if applicable)
	KeyOpWrapKey    KeyOperation = "wrapKey"    // (encrypt key)
	KeyOpUnwrapKey  KeyOperation = "unwrapKey"  // (decrypt key and validate decryption, if applicable)
	KeyOpDeriveKey  KeyOperation = "deriveKey"  // (derive key)
	KeyOpDeriveBits KeyOperation = "deriveBits" // (derive bits not to be used as a key)
)

type KeyOperationList

type KeyOperationList []KeyOperation

func (*KeyOperationList) Accept

func (ops *KeyOperationList) Accept(v interface{}) error

func (*KeyOperationList) Get

type KeyPair

type KeyPair = arrayiter.Pair

type KeyUsageType

type KeyUsageType string

KeyUsageType is used to denote what this key should be used for

const (
	// ForSignature is the value used in the headers to indicate that
	// this key should be used for signatures
	ForSignature KeyUsageType = "sig"
	// ForEncryption is the value used in the headers to indicate that
	// this key should be used for encrypting
	ForEncryption KeyUsageType = "enc"
)

func (*KeyUsageType) Accept

func (k *KeyUsageType) Accept(v interface{}) error

func (KeyUsageType) String

func (k KeyUsageType) String() string

type KeyWithDecodeCtx

type KeyWithDecodeCtx interface {
	SetDecodeCtx(DecodeCtx)
	DecodeCtx() DecodeCtx
}

type MapWhitelist

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

MapWhitelist is a jwk.Whitelist object comprised of a map of strings. If the URL exists in the map, then the URL is allowed to be fetched.

func NewMapWhitelist

func NewMapWhitelist() *MapWhitelist

func (*MapWhitelist) Add

func (w *MapWhitelist) Add(pat string) *MapWhitelist

func (*MapWhitelist) IsAllowed

func (w *MapWhitelist) IsAllowed(u string) bool

type OKPPrivateKey

type OKPPrivateKey interface {
	Key
	FromRaw(interface{}) error
	Crv() jwa.EllipticCurveAlgorithm
	D() []byte
	X() []byte
}

type OKPPublicKey

type OKPPublicKey interface {
	Key
	FromRaw(interface{}) error
	Crv() jwa.EllipticCurveAlgorithm
	X() []byte
}

type Option

type Option = option.Interface

type ParseOption

type ParseOption interface {
	Option
	// contains filtered or unexported methods
}

ParseOption is a type of Option that can be passed to `jwk.Parse()` ParseOption also implmentsthe `ReadFileOption` and `CacheOption`, and thus safely be passed to `jwk.ReadFile` and `(*jwk.Cache).Configure()`

func WithIgnoreParseError

func WithIgnoreParseError(v bool) ParseOption

WithIgnoreParseError is only applicable when used with `jwk.Parse()` (i.e. to parse JWK sets). If passed to `jwk.ParseKey()`, the function will return an error no matter what the input is.

DO NOT USE WITHOUT EXHAUSTING ALL OTHER ROUTES FIRST.

The option specifies that errors found during parsing of individual keys are ignored. For example, if you had keys A, B, C where B is invalid (e.g. it does not contain the required fields), then the resulting JWKS will contain keys A and C only.

This options exists as an escape hatch for those times when a key in a JWKS that is irrelevant for your use case is causing your JWKS parsing to fail, and you want to get to the rest of the keys in the JWKS.

Again, DO NOT USE unless you have exhausted all other routes. When you use this option, you will not be able to tell if you are using a faulty JWKS, except for when there are JSON syntax errors.

func WithPEM

func WithPEM(v bool) ParseOption

WithPEM specifies that the input to `Parse()` is a PEM encoded key.

func WithTypedField

func WithTypedField(name string, object interface{}) ParseOption

WithTypedField allows a private field to be parsed into the object type of your choice. It works much like the RegisterCustomField, but the effect is only applicable to the jwt.Parse function call which receives this option.

While this can be extremely useful, this option should be used with caution: There are many caveats that your entire team/user-base needs to be aware of, and therefore in general its use is discouraged. Only use it when you know what you are doing, and you document its use clearly for others.

First and foremost, this is a "per-object" option. Meaning that given the same serialized format, it is possible to generate two objects whose internal representations may differ. That is, if you parse one _WITH_ the option, and the other _WITHOUT_, their internal representation may completely differ. This could potentially lead to problems.

Second, specifying this option will slightly slow down the decoding process as it needs to consult multiple definitions sources (global and local), so be careful if you are decoding a large number of tokens, as the effects will stack up.

type PostFetchFunc

type PostFetchFunc func(string, Set) (Set, error)

PostFetchFunc is a PostFetcher based on a functon.

func (PostFetchFunc) PostFetch

func (f PostFetchFunc) PostFetch(u string, set Set) (Set, error)

type PostFetcher

type PostFetcher interface {
	// PostFetch revceives the URL and the JWKS, after a successful
	// fetch and parse.
	//
	// It should return a `Set`, optionally modified, to be stored
	// in the cache for subsequent use
	PostFetch(string, Set) (Set, error)
}

PostFetcher is an interface for objects that want to perform operations on the `Set` that was fetched.

type PublicKeyer

type PublicKeyer interface {
	// PublicKey creates the corresponding PublicKey type for this object.
	// All fields are copied onto the new public key, except for those that are not allowed.
	// Returned value must not be the receiver itself.
	PublicKey() (Key, error)
}

type RSAPrivateKey

type RSAPrivateKey interface {
	Key
	FromRaw(*rsa.PrivateKey) error
	D() []byte
	DP() []byte
	DQ() []byte
	E() []byte
	N() []byte
	P() []byte
	Q() []byte
	QI() []byte
}

type RSAPublicKey

type RSAPublicKey interface {
	Key
	FromRaw(*rsa.PublicKey) error
	E() []byte
	N() []byte
}

type ReadFileOption

type ReadFileOption interface {
	Option
	// contains filtered or unexported methods
}

ReadFileOption is a type of `Option` that can be passed to `jwk.ReadFile`

func WithFS

func WithFS(v fs.FS) ReadFileOption

WithFS specifies the source `fs.FS` object to read the file from.

type RegexpWhitelist

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

RegexpWhitelist is a jwk.Whitelist object comprised of a list of *regexp.Regexp objects. All entries in the list are tried until one matches. If none of the *regexp.Regexp objects match, then the URL is deemed unallowed.

func NewRegexpWhitelist

func NewRegexpWhitelist() *RegexpWhitelist

func (*RegexpWhitelist) Add

func (*RegexpWhitelist) IsAllowed

func (w *RegexpWhitelist) IsAllowed(u string) bool

IsAlloed returns true if any of the patterns in the whitelist returns true.

type RegisterOption

type RegisterOption interface {
	Option
	// contains filtered or unexported methods
}

RegisterOption desribes options that can be passed to `(jwk.Cache).Register()`

func WithMinRefreshInterval

func WithMinRefreshInterval(v time.Duration) RegisterOption

WithMinRefreshInterval specifies the minimum refresh interval to be used when using `jwk.Cache`. This value is ONLY used if you did not specify a user-supplied static refresh interval via `WithRefreshInterval`.

This value is used as a fallback value when tokens are refreshed.

When we fetch the key from a remote URL, we first look at the max-age directive from Cache-Control response header. If this value is present, we compare the max-age value and the value specified by this option and take the larger one.

Next we check for the Expires header, and similarly if the header is present, we compare it against the value specified by this option, and take the larger one.

Finally, if neither of the above headers are present, we use the value specified by this option as the next refresh timing

If unspecified, the minimum refresh interval is 1 hour

func WithPostFetcher

func WithPostFetcher(v PostFetcher) RegisterOption

WithPostFetcher specifies the PostFetcher object to be used on the jwk.Set object obtained in `jwk.Cache`. This option can be used to, for example, modify the jwk.Set to give it key IDs or algorithm names after it has been fetched and parsed, but before it is cached.

func WithRefreshInterval

func WithRefreshInterval(v time.Duration) RegisterOption

WithRefreshInterval specifies the static interval between refreshes of jwk.Set objects controlled by jwk.Cache.

Providing this option overrides the adaptive token refreshing based on Cache-Control/Expires header (and jwk.WithMinRefreshInterval), and refreshes will *always* happen in this interval.

type Set

type Set interface {
	// AddKey adds the specified key. If the key already exists in the set,
	// an error is returned.
	AddKey(Key) error

	// Clear resets the list of keys associated with this set, emptying the
	// internal list of `jwk.Key`s, as well as clearing any other non-key
	// fields
	Clear() error

	// Get returns the key at index `idx`. If the index is out of range,
	// then the second return value is false.
	Key(int) (Key, bool)

	// Get returns the value of a private field in the key set.
	//
	// For the purposes of a key set, any field other than the "keys" field is
	// considered to be a private field. In other words, you cannot use this
	// method to directly access the list of keys in the set
	Get(string) (interface{}, bool)

	// Set sets the value of a single field.
	//
	// This method, which takes an `interface{}`, exists because
	// these objects can contain extra _arbitrary_ fields that users can
	// specify, and there is no way of knowing what type they could be.
	Set(string, interface{}) error

	// Remove removes the specified non-key field from the set.
	// Keys may not be removed using this method. See RemoveKey for
	// removing keys.
	Remove(string) error

	// Index returns the index where the given key exists, -1 otherwise
	Index(Key) int

	// Len returns the number of keys in the set
	Len() int

	// LookupKeyID returns the first key matching the given key id.
	// The second return value is false if there are no keys matching the key id.
	// The set *may* contain multiple keys with the same key id. If you
	// need all of them, use `Iterate()`
	LookupKeyID(string) (Key, bool)

	// RemoveKey removes the key from the set.
	// RemoveKey returns an error when the specified key does not exist
	// in set.
	RemoveKey(Key) error

	// Keys creates an iterator to iterate through all keys in the set.
	Keys(context.Context) KeyIterator

	// Iterate creates an iterator to iterate through all fields other than the keys
	Iterate(context.Context) HeaderIterator

	// Clone create a new set with identical keys. Keys themselves are not cloned.
	Clone() (Set, error)
}

Set represents JWKS object, a collection of jwk.Key objects.

Sets can be safely converted to and from JSON using the standard `"encoding/json".Marshal` and `"encoding/json".Unmarshal`. However, if you do not know if the payload contains a single JWK or a JWK set, consider using `jwk.Parse()` to always get a `jwk.Set` out of it.

Since v1.2.12, JWK sets with private parameters can be parsed as well. Such private parameters can be accessed via the `Field()` method. If a resource contains a single JWK instead of a JWK set, private parameters are stored in _both_ the resulting `jwk.Set` object and the `jwk.Key` object .

func Fetch

func Fetch(ctx context.Context, u string, options ...FetchOption) (Set, error)

Fetch fetches a JWK resource specified by a URL. The url must be pointing to a resource that is supported by `net/http`.

If you are using the same `jwk.Set` for long periods of time during the lifecycle of your program, and would like to periodically refresh the contents of the object with the data at the remote resource, consider using `jwk.Cache`, which automatically refreshes jwk.Set objects asynchronously.

Please note that underneath the `jwk.Fetch` function, it uses a global object that spawns goroutines that are present until the go runtime exits. Initially this global variable is uninitialized, but upon calling `jwk.Fetch` once, it is initialized and goroutines are spawned. If you want to control the lifetime of these goroutines, you can call `jwk.SetGlobalFetcher` with a custom fetcher which is tied to a `context.Context` object that you can control.

func NewCachedSet

func NewCachedSet(cache *Cache, url string) Set

func NewSet

func NewSet() Set

NewSet creates and empty `jwk.Set` object

func Parse

func Parse(src []byte, options ...ParseOption) (Set, error)

Parse parses JWK from the incoming []byte.

For JWK sets, this is a convenience function. You could just as well call `json.Unmarshal` against an empty set created by `jwk.NewSet()` to parse a JSON buffer into a `jwk.Set`.

This function exists because many times the user does not know before hand if a JWK(s) resource at a remote location contains a single JWK key or a JWK set, and `jwk.Parse()` can handle either case, returning a JWK Set even if the data only contains a single JWK key

If you are looking for more information on how JWKs are parsed, or if you know for sure that you have a single key, please see the documentation for `jwk.ParseKey()`.

func ParseReader

func ParseReader(src io.Reader, options ...ParseOption) (Set, error)

ParseReader parses a JWK set from the incoming byte buffer.

func ParseString

func ParseString(s string, options ...ParseOption) (Set, error)

ParseString parses a JWK set from the incoming string.

func PublicSetOf

func PublicSetOf(v Set) (Set, error)

PublicSetOf returns a new jwk.Set consisting of public keys of the keys contained in the set.

This is useful when you are generating a set of private keys, and you want to generate the corresponding public versions for the users to verify with.

Be aware that all fields will be copied onto the new public key. It is the caller's responsibility to remove any fields, if necessary.

func ReadFile

func ReadFile(path string, options ...ReadFileOption) (Set, error)

type SymmetricKey

type SymmetricKey interface {
	Key
	FromRaw([]byte) error
	Octets() []byte
}

type Transformer

type Transformer = httprc.Transformer

type Whitelist

type Whitelist = httprc.Whitelist

Whitelist describes a set of rules that allows users to access a particular URL. By default all URLs are blocked for security reasons. You will HAVE to provide some sort of whitelist. See the documentation for github.com/lestrrat-go/httprc for more details.

type WhitelistFunc

type WhitelistFunc func(string) bool

WhitelistFunc is a jwk.Whitelist object based on a function. You can perform any sort of check against the given URL to determine if it can be fetched or not.

func (WhitelistFunc) IsAllowed

func (w WhitelistFunc) IsAllowed(u string) bool

Jump to

Keyboard shortcuts

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