const ( ECDSACrvKey = "crv" ECDSADKey = "d" ECDSAXKey = "x" ECDSAYKey = "y" )
const ( KeyTypeKey = "kty" KeyUsageKey = "use" KeyOpsKey = "key_ops" AlgorithmKey = "alg" KeyIDKey = "kid" X509URLKey = "x58" X509CertChainKey = "x5c" X509CertThumbprintKey = "x5t" X509CertThumbprintS256Key = "x5t#S256" )
const ( OKPCrvKey = "crv" OKPDKey = "d" OKPXKey = "x" )
const ( RSADKey = "d" RSADPKey = "dp" RSADQKey = "dq" RSAEKey = "e" RSANKey = "n" RSAPKey = "p" RSAQIKey = "qi" RSAQKey = "q" )
const (
SymmetricOctetsKey = "k"
)
func AssignKeyID(key Key, options ...Option) 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 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(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.
Currently only EC (including Ed25519) and RSA keys (and jwk.Set comprised of these key types) are supported.
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(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)
AutoRefresh is a container that keeps track of jwk.Set object by their source URLs. The jwk.Set objects are refreshed automatically behind the scenes.
Before retrieving the jwk.Set objects, the user must pre-register the URLs they intend to use by calling `Configure()`
ar := jwk.NewAutoRefresh(ctx) ar.Configure(url, options...)
Once registered, you can call `Fetch()` to retrieve the jwk.Set object.
All JWKS objects that are retrieved via the auto-fetch mechanism should be treated read-only, as they are shared among the consumers and this object.
type AutoRefresh struct {
// contains filtered or unexported fields
}
func NewAutoRefresh(ctx context.Context) *AutoRefresh
NewAutoRefresh creates a container that keeps track of JWKS objects which are automatically refreshed.
The context object in the argument controls the life-span of the auto-refresh worker. If you are using this in a long running process, this should mostly be set to a context that ends when the main loop/part of your program exits:
func MainLoop() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ar := jwk.AutoRefresh(ctx) for ... { ... } }
func (af *AutoRefresh) Configure(url string, options ...AutoRefreshOption)
Configure registers the url to be controlled by AutoRefresh, and also sets any options associated to it.
Note that options are treated as a whole -- you can't just update one value. For example, if you did:
ar.Configure(url, jwk.WithHTTPClient(...)) ar.Configure(url, jwk.WithRefreshInterval(...))
The the end result is that `url` is ONLY associated with the options given in the second call to `Configure()`, i.e. `jwk.WithRefreshInterval`. The other unspecified options, including the HTTP client, is set to their default values.
Configuration must propagate between goroutines, and therefore are not atomic (But changes should be felt "soon enough" for practical purposes)
func (af *AutoRefresh) ErrorSink(ch chan AutoRefreshError)
ErrorSink sets a channel to receive JWK fetch errors, if any. Only the errors that occurred *after* the channel was set will be sent.
The user is responsible for properly draining the channel. If the channel is not drained properly, errors will be discarded.
To disable, set a nil channel.
func (af *AutoRefresh) Fetch(ctx context.Context, url string) (Set, error)
Fetch returns a jwk.Set from the given url.
If it has previously been fetched, then a cached value is returned.
If this the first time `url` was requested, an HTTP request will be sent, synchronously.
When accessed via multiple goroutines concurrently, and the cache has not been populated yet, only the first goroutine is allowed to perform the initialization (HTTP fetch and cache population). All other goroutines will be blocked until the operation is completed.
DO NOT modify the jwk.Set object returned by this method, as the objects are shared among all consumers and the backend goroutine
func (af *AutoRefresh) IsRegistered(url string) bool
IsRegistered checks if `url` is registered already.
func (af *AutoRefresh) Refresh(ctx context.Context, url string) (Set, error)
Refresh is the same as Fetch(), except that HTTP fetching is done synchronously.
This is useful when you want to force an HTTP fetch instead of waiting for the background goroutine to do it, for example when you want to make sure the AutoRefresh cache is warmed up before starting your main loop
func (af *AutoRefresh) Remove(url string) error
Remove removes `url` from the list of urls being watched by jwk.AutoRefresh. If the url is not already registered, returns an error.
func (af *AutoRefresh) Snapshot() <-chan TargetSnapshot
type AutoRefreshError struct { Error error URL string }
AutoRefreshOption is a type of Option that can be passed to the AutoRefresh object.
type AutoRefreshOption interface { Option // contains filtered or unexported methods }
func WithMinRefreshInterval(d time.Duration) AutoRefreshOption
WithMinRefreshInterval specifies the minimum refresh interval to be used when using AutoRefresh. 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 WithRefreshInterval(d time.Duration) AutoRefreshOption
WithRefreshInterval specifies the static interval between refreshes of jwk.Set objects controlled by jwk.AutoRefresh.
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 CertificateChain struct {
// contains filtered or unexported fields
}
func (c *CertificateChain) Accept(v interface{}) error
func (c CertificateChain) Get() []*x509.Certificate
func (c CertificateChain) MarshalJSON() ([]byte, error)
func (c *CertificateChain) UnmarshalJSON(buf []byte) error
type DecodeCtx interface { json.DecodeCtx IgnoreParseError() bool }
type ECDSAPrivateKey interface { Key FromRaw(*ecdsa.PrivateKey) error Crv() jwa.EllipticCurveAlgorithm D() []byte X() []byte Y() []byte }
func NewECDSAPrivateKey() ECDSAPrivateKey
type ECDSAPublicKey interface { Key FromRaw(*ecdsa.PublicKey) error Crv() jwa.EllipticCurveAlgorithm X() []byte Y() []byte }
func NewECDSAPublicKey() ECDSAPublicKey
FetchOption is a type of Option that can be passed to `jwk.Fetch()` FetchOption also implements the `AutoRefreshOption`, and thus can safely be passed to `(*jwk.AutoRefresh).Configure()`
type FetchOption interface { AutoRefreshOption // contains filtered or unexported methods }
func WithFetchBackoff(v backoff.Policy) FetchOption
WithFetchBackoff specifies the backoff policy to use when refreshing a JWKS from a remote server fails.
This does not have any effect on initial `Fetch()`, or any of the `Refresh()` calls -- the backoff is applied ONLY on the background refreshing goroutine.
func WithFetchWhitelist(w 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.NewAutoRefresh()`, and `(*jwk.AutoRefresh).Configure()`
func WithHTTPClient(cl HTTPClient) FetchOption
WithHTTPClient allows users to specify the "net/http".Client object that is used when fetching jwk.Set objects.
HTTPClient specifies the minimum interface that is required for our JWK fetching tools.
type HTTPClient interface { Do(*http.Request) (*http.Response, error) }
type HeaderIterator = mapiter.Iterator
type HeaderPair = mapiter.Pair
type HeaderVisitor = iter.MapVisitor
type HeaderVisitorFunc = iter.MapVisitorFunc
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
type InsecureWhitelist struct{}
func (InsecureWhitelist) IsAllowed(string) bool
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
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 // 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 `kid` 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 `alg` of a JWK Algorithm() string // KeyID returns `kid` of a JWK KeyID() string // X509URL returns `x58` of a JWK X509URL() string // X509CertChain returns `x5c` of a JWK X509CertChain() []*x509.Certificate // X509CertThumbprint returns `x5t` of a JWK X509CertThumbprint() string // X509CertThumbprintS256 returns `x5t#S256` of a JWK X509CertThumbprintS256() string // contains filtered or unexported methods }
func New(key interface{}) (Key, error)
New 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:
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(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 = arrayiter.Iterator
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 []KeyOperation
func (ops *KeyOperationList) Accept(v interface{}) error
func (ops *KeyOperationList) Get() KeyOperationList
type KeyPair = arrayiter.Pair
KeyUsageType is used to denote what this key should be used for
type KeyUsageType string
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 (k *KeyUsageType) Accept(v interface{}) error
func (k KeyUsageType) String() string
type KeyWithDecodeCtx interface { SetDecodeCtx(DecodeCtx) DecodeCtx() DecodeCtx }
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.
type MapWhitelist struct {
// contains filtered or unexported fields
}
func NewMapWhitelist() *MapWhitelist
func (w *MapWhitelist) Add(pat string) *MapWhitelist
func (w *MapWhitelist) IsAllowed(u string) bool
type OKPPrivateKey interface { Key FromRaw(interface{}) error Crv() jwa.EllipticCurveAlgorithm D() []byte X() []byte }
func NewOKPPrivateKey() OKPPrivateKey
type OKPPublicKey interface { Key FromRaw(interface{}) error Crv() jwa.EllipticCurveAlgorithm X() []byte }
func NewOKPPublicKey() OKPPublicKey
type Option = option.Interface
func WithThumbprintHash(h crypto.Hash) Option
ParseOption is a type of Option that can be passed to `jwk.Parse()` ParseOption also implmentsthe `ReadFileOPtion` and `AutoRefreshOption`, and thus safely be passed to `jwk.ReadFile` and `(*jwk.AutoRefresh).Configure()`
type ParseOption interface { ReadFileOption AutoRefreshOption // contains filtered or unexported methods }
func WithIgnoreParseError(b 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(v bool) ParseOption
WithPEM specifies that the input to `Parse()` is a PEM encoded key.
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 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 interface { Key FromRaw(*rsa.PrivateKey) error D() []byte DP() []byte DQ() []byte E() []byte N() []byte P() []byte Q() []byte QI() []byte }
func NewRSAPrivateKey() RSAPrivateKey
type RSAPublicKey interface { Key FromRaw(*rsa.PublicKey) error E() []byte N() []byte }
func NewRSAPublicKey() RSAPublicKey
ReadFileOption describes options that can be passed to ReadFile.
type ReadFileOption interface { Option // contains filtered or unexported methods }
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.
type RegexpWhitelist struct {
// contains filtered or unexported fields
}
func NewRegexpWhitelist() *RegexpWhitelist
func (w *RegexpWhitelist) Add(pat *regexp.Regexp) *RegexpWhitelist
func (w *RegexpWhitelist) IsAllowed(u string) bool
IsAlloed returns true if any of the patterns in the whitelist returns true.
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 .
type Set interface { // Add adds the specified key. If the key already exists in the set, it is // not added. // This method will be renamed to `AddKey(Key)` in a future major release. Add(Key) bool // Clear resets the list of keys associated with this set, emptying the // internal list of `jwk.Key`s // This method will be changed in the future to clear all contents in the // `jwk.Set` instead of just the keys. Clear() // Get returns the key at index `idx`. If the index is out of range, // then the second return value is false. // This method will be renamed to `Key(int)` in a future major release. Get(int) (Key, bool) // Field 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 // // This method will be renamed to `Get(string)` in a future major release. Field(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 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. // 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) // Remove removes the key from the set. Remove(Key) bool // Iterate creates an iterator to iterate through all keys in the set. Iterate(context.Context) KeyIterator // Clone create a new set with identical keys. Keys themselves are not cloned. Clone() (Set, error) }
func Fetch(ctx context.Context, urlstring 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.AutoRefresh`, which automatically refreshes jwk.Set objects asynchronously.
See the list of `jwk.FetchOption`s for various options to tweak the behavior, including providing alternate HTTP Clients, setting a backoff, and using whitelists.
func NewSet() Set
NewSet creates and empty `jwk.Set` object
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(src io.Reader, options ...ParseOption) (Set, error)
ParseReader parses a JWK set from the incoming byte buffer.
func ParseString(s string, options ...ParseOption) (Set, error)
ParseString parses a JWK set from the incoming string.
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(path string, options ...ReadFileOption) (Set, error)
type SymmetricKey interface { Key FromRaw([]byte) error Octets() []byte }
func NewSymmetricKey() SymmetricKey
TargetSnapshot is the structure returned by the Snapshot method. It contains information about a url that has been configured in AutoRefresh.
type TargetSnapshot struct { URL string NextRefresh time.Time LastRefresh time.Time }
Whitelist is an interface for a set of URL whitelists. When provided to JWK fetching operations, urls are checked against this object, and the object must return true for urls to be fetched.
type Whitelist interface { IsAllowed(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.
type WhitelistFunc func(string) bool
func (w WhitelistFunc) IsAllowed(u string) bool