391 lines
14 KiB
Markdown
391 lines
14 KiB
Markdown
|
# Incompatible Changes from v1 to v2
|
||
|
|
||
|
These are changes that are incompatible with the v1.x.x version.
|
||
|
|
||
|
* [tl;dr](#tldr) - If you don't feel like reading the details -- but you will read the details, right?
|
||
|
* [Detailed List of Changes](#detailed-list-of-changes) - A comprehensive list of changes from v1 to v2
|
||
|
|
||
|
# tl;dr
|
||
|
|
||
|
## JWT
|
||
|
|
||
|
```go
|
||
|
// most basic
|
||
|
jwt.Parse(serialized, jwt.WithKey(alg, key)) // NOTE: verification and validation are ENABLED by default!
|
||
|
jwt.Sign(token, jwt.WithKey(alg,key))
|
||
|
|
||
|
// with a jwk.Set
|
||
|
jwt.Parse(serialized, jwt.WithKeySet(set))
|
||
|
|
||
|
// UseDefault/InferAlgorithm with JWKS
|
||
|
jwt.Parse(serialized, jwt.WithKeySet(set,
|
||
|
jws.WithUseDefault(true), jws.WithInferAlgorithm(true))
|
||
|
|
||
|
// Use `jku`
|
||
|
jwt.Parse(serialized, jwt.WithVerifyAuto(...))
|
||
|
|
||
|
// Any other custom key provisioning (using functions in this
|
||
|
// example, but can be anything that fulfills jws.KeyProvider)
|
||
|
jwt.Parse(serialized, jwt.WithKeyProvider(jws.KeyProviderFunc(...)))
|
||
|
```
|
||
|
|
||
|
## JWK
|
||
|
|
||
|
```go
|
||
|
// jwk.New() was confusing. Renamed to fit the actual implementation
|
||
|
key, err := jwk.FromRaw(rawKey)
|
||
|
|
||
|
// Algorithm() now returns jwa.KeyAlgorithm type. `jws.Sign()`
|
||
|
// and other function that receive JWK algorithm names accept
|
||
|
// this new type, so you can use the same key and do the following
|
||
|
// (previosly you needed to type assert)
|
||
|
jws.Sign(payload, jws.WithKey(key.Algorithm(), key))
|
||
|
|
||
|
// If you need the specific type, type assert
|
||
|
key.Algorithm().(jwa.SignatureAlgorithm)
|
||
|
|
||
|
// jwk.AutoRefresh is no more. Use jwk.Cache
|
||
|
cache := jwk.NewCache(ctx, options...)
|
||
|
|
||
|
// Certificate chains are no longer jwk.CertificateChain type, but
|
||
|
// *(github.com/lestrrat-go/jwx/cert).Chain
|
||
|
cc := key.X509CertChain() // this is *cert.Chain now
|
||
|
```
|
||
|
|
||
|
## JWS
|
||
|
|
||
|
```go
|
||
|
// basic
|
||
|
jws.Sign(payload, jws.WithKey(alg, key))
|
||
|
jws.Sign(payload, jws.WithKey(alg, key), jws.WithKey(alg, key), jws.WithJSON(true))
|
||
|
jws.Verify(signed, jws.WithKey(alg, key))
|
||
|
|
||
|
// other ways to pass the key
|
||
|
jws.Sign(payload, jws.WithKeySet(jwks))
|
||
|
jws.Sign(payload, jws.WithKeyProvider(kp))
|
||
|
|
||
|
// retrieve the key that succeeded in verifying
|
||
|
var keyUsed interface{}
|
||
|
jws.Verify(signed, jws.WithKeySet(jwks), jws.WithKeyUsed(&keyUsed))
|
||
|
```
|
||
|
|
||
|
## JWE
|
||
|
|
||
|
```go
|
||
|
// basic
|
||
|
jwe.Encrypt(payload, jwe.WithKey(alg, key)) // other defaults are infered
|
||
|
jwe.Encrypt(payload, jwe.WithKey(alg, key), jwe.WithKey(alg, key), jwe.WithJSON(true))
|
||
|
jwe.Decrypt(encrypted, jwe.WithKey(alg, key))
|
||
|
|
||
|
// other ways to pass the key
|
||
|
jwe.Encrypt(payload, jwe.WithKeySet(jwks))
|
||
|
jwe.Encrypt(payload, jwe.WithKeyProvider(kp))
|
||
|
|
||
|
// retrieve the key that succeeded in decrypting
|
||
|
var keyUsed interface{}
|
||
|
jwe.Verify(signed, jwe.WithKeySet(jwks), jwe.WithKeyUsed(&keyUsed))
|
||
|
```
|
||
|
|
||
|
# Detailed List of Changes
|
||
|
|
||
|
## Module
|
||
|
|
||
|
* Module now requires go 1.16
|
||
|
|
||
|
* Use of github.com/pkg/errors is no more. If you were relying on bevaior
|
||
|
that depends on the errors being an instance of github.com/pkg/errors
|
||
|
then you need to change your code
|
||
|
|
||
|
* File-generation tools have been moved out of internal/ directories.
|
||
|
These files pre-dates Go modules, and they were in internal/ in order
|
||
|
to avoid being listed in the `go doc` -- however, now that we can
|
||
|
make them separate modules this is no longer necessary.
|
||
|
|
||
|
* New package `cert` has been added to handle `x5c` certificate
|
||
|
chains, and to work with certificates
|
||
|
* cert.Chain to store base64 encoded ASN.1 DER format certificates
|
||
|
* cert.EncodeBase64 to encode ASN.1 DER format certificate using base64
|
||
|
* cert.Create to create a base64 encoded ASN.1 DER format certificates
|
||
|
* cert.Parse to parse base64 encoded ASN.1 DER format certificates
|
||
|
|
||
|
## JWE
|
||
|
|
||
|
* `jwe.Compact()`'s signature has changed to
|
||
|
`jwe.Compact(*jwe.Message, ...jwe.CompactOption)`
|
||
|
|
||
|
* `jwe.JSON()` has been removed. You can generate JSON serialization
|
||
|
using `jwe.Encrypt(jwe.WitJSON())` or `json.Marshal(jwe.Message)`
|
||
|
|
||
|
* `(jwe.Message).Decrypt()` has been removed. Since formatting of the
|
||
|
original serialized message matters (including whitespace), using a parsed
|
||
|
object was inherently confusing.
|
||
|
|
||
|
* `jwe.Encrypt()` can now generate JWE messages in either compact or JSON
|
||
|
forms. By default, the compact form is used. JSON format can be
|
||
|
enabled by using the `jwe.WithJSON` option.
|
||
|
|
||
|
* `jwe.Encrypt()` can now accept multiple keys by passing multiple
|
||
|
`jwe.WithKey()` options. This can be used with `jwe.WithJSON` to
|
||
|
create JWE messages with multiple recipients.
|
||
|
|
||
|
* `jwe.DecryptEncryptOption()` has been renamed to `jwe.EncryptDecryptOption()`.
|
||
|
This is so that it is more uniform with `jws` equivalent of `jws.SignVerifyOption()`
|
||
|
where the producer (`Sign`) comes before the consumer (`Verify`) in the naming
|
||
|
|
||
|
* `jwe.WithCompact` and `jwe.WithJSON` options have been added
|
||
|
to control the serialization format.
|
||
|
|
||
|
* jwe.Decrypt()'s method signature has been changed to `jwt.Decrypt([]byte, ...jwe.DecryptOption) ([]byte, error)`.
|
||
|
These options can be stacked. Therefore, you could configure the
|
||
|
verification process to attempt a static key pair, a JWKS, and only
|
||
|
try other forms if the first two fails, for example.
|
||
|
|
||
|
- For static key pair, use `jwe.WithKey()`
|
||
|
- For static JWKS, use `jwe.WithKeySet()` (NOTE: InferAlgorithmFromKey like in `jws` package is NOT supported)
|
||
|
- For custom, possibly dynamic key provisioning, use `jwe.WithKeyProvider()`
|
||
|
|
||
|
* jwe.Decrypter has been unexported. Users did not need this.
|
||
|
|
||
|
* jwe.WithKeyProvider() has been added to specify arbitrary
|
||
|
code to specify which keys to try.
|
||
|
|
||
|
* jwe.KeyProvider interface has been added
|
||
|
|
||
|
* jwe.KeyProviderFunc has been added
|
||
|
|
||
|
* `WithPostParser()` has been removed. You can achieve the same effect
|
||
|
by using `jwe.WithKeyProvider()`. Because this was the only consumer for
|
||
|
`jwe.DecryptCtx`, this type has been removed as well.
|
||
|
|
||
|
* `x5c` field type has been changed to `*cert.Chain` instead of `[]string`
|
||
|
|
||
|
* Method signature for `jwe.Parse()` has been changed to include options,
|
||
|
but options are currently not used
|
||
|
|
||
|
* `jwe.ReadFile` now supports the option `jwe.WithFS` which allows you to
|
||
|
read data from arbitrary `fs.FS` objects
|
||
|
|
||
|
* jwe.WithKeyUsed has been added to allow users to retrieve
|
||
|
the key used for decryption. This is useful in cases you provided
|
||
|
multiple keys and you want to know which one was successful
|
||
|
|
||
|
## JWK
|
||
|
|
||
|
* `jwk.New()` has been renamed to `jwk.FromRaw()`, which hopefully will
|
||
|
make it easier for the users what the input should be.
|
||
|
|
||
|
* `jwk.Set` has many interface changes:
|
||
|
* Changed methods to match jwk.Key and its semantics:
|
||
|
* Field is now Get() (returns values for arbitrary fields other than keys). Fetching a key is done via Key()
|
||
|
* Remove() now removes arbitrary fields, not keys. to remove keys, use RemoveKey()
|
||
|
* Iterate has been added to iterate through all non-key fields.
|
||
|
* Add is now AddKey(Key) string, and returns an error when the same key is added
|
||
|
* Get is now Key(int) (Key, bool)
|
||
|
* Remove is now RemoveKey(Key) error
|
||
|
* Iterate is now Keys(context.Context) KeyIterator
|
||
|
* Clear is now Clear() error
|
||
|
|
||
|
* `jwk.CachedSet` has been added. You can create a `jwk.Set` that is backed by
|
||
|
`jwk.Cache` so you can do this:
|
||
|
|
||
|
```go
|
||
|
cache := jkw.NewCache(ctx)
|
||
|
cachedSet := jwk.NewCachedSet(cache, jwksURI)
|
||
|
|
||
|
// cachedSet is always the refreshed, cached version from jwk.Cache
|
||
|
jws.Verify(signed, jws.WithKeySet(cachedSet))
|
||
|
```
|
||
|
|
||
|
* `jwk.NewRSAPRivateKey()`, `jwk.NewECDSAPrivateKey()`, etc have been removed.
|
||
|
There is no longer any way to create concrete types of `jwk.Key`
|
||
|
|
||
|
* `jwk.Key` type no longer supports direct unmarshaling via `json.Unmarshal()`,
|
||
|
because you can no longer instantiate concrete `jwk.Key` types. You will need to
|
||
|
use `jwk.ParseKey()`. See the documentation for ways to parse JWKs.
|
||
|
|
||
|
* `(jwk.Key).Algorithm()` is now of `jwk.KeyAlgorithm` type. This field used
|
||
|
to be `string` and therefore could not be passed directly to `jwt.Sign()`
|
||
|
`jws.Sign()`, `jwe.Encrypt()`, et al. This is no longer the case, and
|
||
|
now you can pass it directly. See
|
||
|
https://github.com/lestrrat-go/jwx/blob/v2/docs/99-faq.md#why-is-jwkkeyalgorithm-and-jwakeyalgorithm-so-confusing
|
||
|
for more details
|
||
|
|
||
|
* `jwk.Fetcher` and `jwk.FetchFunc` has been added.
|
||
|
They represent something that can fetch a `jwk.Set`
|
||
|
|
||
|
* `jwk.CertificateChain` has been removed, use `*cert.Chain`
|
||
|
* `x5c` field type has been changed to `*cert.Chain` instead of `[]*x509.Certificate`
|
||
|
|
||
|
* `jwk.ReadFile` now supports the option `jwk.WithFS` which allows you to
|
||
|
read data from arbitrary `fs.FS` objects
|
||
|
|
||
|
* Added `jwk.PostFetcher`, `jwk.PostFetchFunc`, and `jwk.WithPostFetch` to
|
||
|
allow users to get at the `jwk.Set` that was fetched in `jwk.Cache`.
|
||
|
This will make it possible for users to supply extra information and edit
|
||
|
`jwk.Set` after it has been fetched and parsed, but before it is cached.
|
||
|
You could, for example, modify the `alg` field so that it's easier to
|
||
|
work with when you use it in `jws.Verify` later.
|
||
|
|
||
|
* Reworked `jwk.AutoRefresh` in terms of `github.com/lestrrat-go/httprc`
|
||
|
and renamed it `jwk.Cache`.
|
||
|
|
||
|
Major difference between `jwk.AutoRefresh` and `jwk.Cache` is that while
|
||
|
former used one `time.Timer` per resource, the latter uses a static timer
|
||
|
(based on `jwk.WithRefreshWindow()` value, default 15 minutes) that periodically
|
||
|
refreshes all resources that were due to be refreshed within that time frame.
|
||
|
|
||
|
This method may cause your updates to happen slightly later, but uses significantly
|
||
|
less resources and is less prone to clogging.
|
||
|
|
||
|
* Reimplemented `jwk.Fetch` in terms of `github.com/lestrrat-go/httprc`.
|
||
|
|
||
|
* Previously `jwk.Fetch` and `jwk.AutoRefresh` respected backoff options,
|
||
|
but this has been removed. This is to avoid unwanted clogging of the fetch workers
|
||
|
which is the default processing mode in `github.com/lestrrat-go/httprc`.
|
||
|
|
||
|
If you are using backoffs, you need to control your inputs more carefully so as to
|
||
|
not clog your fetch queue, and therefore you should be writing custom code that
|
||
|
suits your needs
|
||
|
|
||
|
## JWS
|
||
|
|
||
|
* `jws.Sign()` can now generate JWS messages in either compact or JSON
|
||
|
forms. By default, the compact form is used. JSON format can be
|
||
|
enabled by using the `jws.WithJSON` option.
|
||
|
|
||
|
* `jws.Sign()` can now accept multiple keys by passing multiple
|
||
|
`jws.WithKey()` options. This can be used with `jws.WithJSON` to
|
||
|
create JWS messages with multiple signatures.
|
||
|
|
||
|
* `jws.WithCompact` and `jws.WithJSON` options have been added
|
||
|
to control the serialization format.
|
||
|
|
||
|
* jws.Verify()'s method signature has been changed to `jwt.Verify([]byte, ...jws.VerifyOption) ([]byte, error)`.
|
||
|
These options can be stacked. Therefore, you could configure the
|
||
|
verification process to attempt a static key pair, a JWKS, and only
|
||
|
try other forms if the first two fails, for example.
|
||
|
|
||
|
- For static key pair, use `jws.WithKey()`
|
||
|
- For static JWKS, use `jws.WithKeySet()`
|
||
|
- For enabling verification using `jku`, use `jws.WithVerifyAuto()`
|
||
|
- For custom, possibly dynamic key provisioning, use `jws.WithKeyProvider()`
|
||
|
|
||
|
* jws.WithVerify() has been removed.
|
||
|
|
||
|
* jws.WithKey() has been added to specify an algorithm + key to
|
||
|
verify the payload with.
|
||
|
|
||
|
* jws.WithKeySet() has been added to specify a JWKS to be used for
|
||
|
verification. By default `kid` AND `alg` must match between the signature
|
||
|
and the key.
|
||
|
|
||
|
The option can take further suboptions:
|
||
|
|
||
|
```go
|
||
|
jws.Parse(serialized,
|
||
|
jws.WithKeySet(set,
|
||
|
// by default `kid` is required. set false to disable.
|
||
|
jws.WithRequireKid(false),
|
||
|
// optionally skip matching kid if there's exactly one key in set
|
||
|
jws.WithUseDefault(true),
|
||
|
// infer algorithm name from key type
|
||
|
jws.WithInferAlgorithm(true),
|
||
|
),
|
||
|
)
|
||
|
```
|
||
|
|
||
|
* `jws.VerifuAuto` has been removed in favor of using
|
||
|
`jws.WithVerifyAuto` option with `jws.Verify()`
|
||
|
|
||
|
* `jws.WithVerifyAuto` has been added to enable verification
|
||
|
using `jku`.
|
||
|
|
||
|
The first argument must be a jwk.Fetcher object, but can be
|
||
|
set to `nil` to use the default implementation which is `jwk.Fetch`
|
||
|
|
||
|
The rest of the arguments are treated as options passed to the
|
||
|
`(jwk.Fetcher).Fetch()` function.
|
||
|
|
||
|
* Remove `jws.WithPayloadSigner()`. This should be completely repleceable
|
||
|
using `jws.WithKey()`
|
||
|
|
||
|
* jws.WithKeyProvider() has been added to specify arbitrary
|
||
|
code to specify which keys to try.
|
||
|
|
||
|
* jws.KeyProvider interface has been added
|
||
|
|
||
|
* jws.KeyProviderFunc has been added
|
||
|
|
||
|
* jws.WithKeyUsed has been added to allow users to retrieve
|
||
|
the key used for verification. This is useful in cases you provided
|
||
|
multiple keys and you want to know which one was successful
|
||
|
|
||
|
* `x5c` field type has been changed to `*cert.Chain` instead of `[]string`
|
||
|
|
||
|
* `jws.ReadFile` now supports the option `jws.WithFS` which allows you to
|
||
|
read data from arbitrary `fs.FS` objects
|
||
|
|
||
|
## JWT
|
||
|
|
||
|
* `jwt.Parse` now verifies the signature and validates the token
|
||
|
by default. You must disable it explicitly using `jwt.WithValidate(false)`
|
||
|
and/or `jwt.WithVerify(false)` if you only want to parse the JWT message.
|
||
|
|
||
|
If you don't want either, a convenience function `jwt.ParseInsecure`
|
||
|
has been added.
|
||
|
|
||
|
* `jwt.Parse` can only parse raw JWT (JSON) or JWS (JSON or Compact).
|
||
|
It no longer accepts JWE messages.
|
||
|
|
||
|
* `jwt.WithDecrypt` has been removed
|
||
|
|
||
|
* `jwt.WithJweHeaders` has been removed
|
||
|
|
||
|
* `jwt.WithVerify()` has been renamed to `jwt.WithKey()`. The option can
|
||
|
be used for signing, encryption, and parsing.
|
||
|
|
||
|
* `jwt.Validator` has been changed to return `jwt.ValidationError`.
|
||
|
If you provide a custom validator, you should wrap the error with
|
||
|
`jwt.NewValidationError()`
|
||
|
|
||
|
* `jwt.UseDefault()` has been removed. You should use `jws.WithUseDefault()`
|
||
|
as a suboption in the `jwt.WithKeySet()` option.
|
||
|
|
||
|
```go
|
||
|
jwt.Parse(serialized, jwt.WithKeySet(set, jws.WithUseDefault(true)))
|
||
|
```
|
||
|
|
||
|
* `jwt.InferAlgorithmFromKey()` has been removed. You should use
|
||
|
`jws.WithInferAlgorithmFromKey()` as a suboption in the `jwt.WithKeySet()` option.
|
||
|
|
||
|
```go
|
||
|
jwt.Parse(serialized, jwt.WithKeySet(set, jws.WithInferAlgorithmFromKey(true)))
|
||
|
```
|
||
|
|
||
|
* jwt.WithKeySetProvider has been removed. Use `jwt.WithKeyProvider()`
|
||
|
instead. If jwt.WithKeyProvider seems a bit complicated, use a combination of
|
||
|
JWS parse, no-verify/validate JWT parse, and an extra JWS verify:
|
||
|
|
||
|
```go
|
||
|
msg, _ := jws.Parse(signed)
|
||
|
token, _ := jwt.Parse(msg.Payload(), jwt.WithVerify(false), jwt.WithValidate(false))
|
||
|
// Get information out of token, for example, `iss`
|
||
|
switch token.Issuer() {
|
||
|
case ...:
|
||
|
jws.Verify(signed, jwt.WithKey(...))
|
||
|
}
|
||
|
```
|
||
|
|
||
|
* `jwt.WithHeaders` and `jwt.WithJwsHeaders` have been removed.
|
||
|
You should be able to use the new `jwt.WithKey` option to pass headers
|
||
|
|
||
|
* `jwt.WithSignOption` and `jwt.WithEncryptOption` have been added as
|
||
|
escape hatches for options that are declared in `jws` and `jwe` packages
|
||
|
but not in `jwt`
|
||
|
|
||
|
* `jwt.ReadFile` now supports the option `jwt.WithFS` which allows you to
|
||
|
read data from arbitrary `fs.FS` objects
|
||
|
|
||
|
* `jwt.Sign()` has been changed so that it works more like the new `jws.Sign()`
|
||
|
|