Changes
=======

v3 has many incompatibilities with v2. To see the full list of differences between
v2 and v3, please read the Changes-v3.md file (https://github.com/lestrrat-go/jwx/blob/develop/v3/Changes-v3.md)

v3.1.1 7 May 2026
  * [jws] Coordinated RFC 7797 `b64=false` handling pass: `jws.Verify`
    rejects payloads with `b64=false` unless `b64` is also listed in
    `crit`; `jws.Sign` auto-declares `b64` in `crit` when emitting
    `b64=false`; `Message.MarshalJSON` honors `b64=false` instead of
    silently re-encoding; `jws.VerifyCompactFast` refuses any compact
    JWS carrying `b64` (the fast path doesn't process extension
    headers); and `b64` is now declared as a typed boolean header
    field rather than handled ad-hoc.
    (#2081, #2087, #2102, #2104, #2106)

  * [jws] Reject malformed general-form JSON-serialized JWS: inputs
    with a top-level `header` member as a sibling of `signatures` are
    rejected (the spec only permits `header` inside per-signature
    objects), as are inputs whose `protected` member is a literal
    JSON object instead of a base64url-encoded string.
    (#2089, #2108)

  * [jws] `jws.AlgorithmsForKey` failures from unclassifiable keys
    are now wrapped in a typed sentinel so callers can branch on
    "couldn't categorize this key" without string matching the error
    message. (#2110)

  * [jws] Verify error-shape consistency: `VerifyCompactFast`
    refusals now match the `jws.VerifyError()` taxonomy used by the
    slow path, fan-out verify errors name the loose `WithKeySet`
    options that were tried, multi-signature `b64` mismatches name
    the offending signature index and conflicting value, and the
    compact `b64=false`+payload-contains-`.` error references RFC
    7797 §5.2 and points at `WithDetachedPayload`.
    (#2083, #2085, #2114)

  * [jws] Keys fetched via the `jku` header are no longer accepted
    for signature verification when the JWK declares `use=enc`.
    (#2060)

  * [jws][jwe] `jws.VerifyMessage` and `jwe.DecryptMessage` observe
    context cancellation between loop iterations rather than only at
    boundaries. Long fan-out verify/decrypt loops now respond to a
    cancelled context promptly. (#2112, #2117)

  * [jwe] Reject PBES2 messages whose `p2c` (iteration count) does
    not parse cleanly into int64 or violates the configured bound.
    The error now names the violated bound (min vs max) instead of
    the generic "out of range". (#2119)

  * [jwe] `jwe.WithKey()` validates the alg-vs-key shape at option
    construction time rather than during encryption, so misuse
    surfaces at the call site instead of inside the encrypt loop.
    (#2121)

  * [jwe] Decrypt error-path cleanup: per-key failures from key-set
    providers are surfaced via `errors.Join` so each underlying
    error remains inspectable; the joined error count is bounded to
    keep diagnostics readable; the redundant outer `Decrypt:` prefix
    is dropped; and the compression-cap error names the
    "decompressed" payload, the option, and the size.
    (#2123, #2125, #2127)

  * [jwe] Add `jwe.WithDisabledKeyAlgorithms(...)` as a global
    policy hook (`jwe.Configure(...)` or per-call) for refusing
    specific key-management algorithms across all `jwe.Decrypt`
    calls. (#2129)

  * [jwe] Reject messages whose protected-header `alg` conflicts
    with a per-recipient `alg`. The previous behavior silently
    preferred the protected-header value. (#2052)

  * [jwk] Stop duplicating JWK fields at the JWKS top level on
    parse. A JWKS whose top-level object carries fields with the
    same names as JWK members no longer copies those values into
    every key in `keys`. (#2133)

  * [jwk] A custom `jwk.KeyParser` returning `(nil, nil)` now
    signals "continue to the next parser" rather than "successfully
    produced a nil key". Callers no longer end up with a nil
    `jwk.Key` from a successful parse when an extension returns the
    empty pair. (#2140)

  * [jwk] Stream the JWKS `keys` array with a cap-before-allocate
    strategy. Inputs respect `WithMaxKeys` before any unbounded
    slice growth, and bounded-size JWKS no longer over-allocate
    based on attacker-controlled length hints. (#2137)

  * [jwk] Wrap `jwk.ParseKey` errors with the `jwk.ParseError`
    sentinel so callers can branch on parse-vs-other failures with
    `errors.Is(err, jwk.ParseError())`. (#2135)

  * [jwk] ECDSA public keys whose X / Y coordinates exceed the
    curve's byte length are rejected with a typed error instead of
    reaching curve arithmetic with an out-of-range `big.Int`.
    (#2050)

  * [jwt] `jwt.ParseRequest` no longer skips the request body when
    the request uses chunked transfer encoding. The
    Content-Length-based fast path previously bypassed `ParseForm`
    for chunked requests even when `WithFormKey` was supplied.
    (#2091)

  * [jwt] Pedantic mode (`jwt.WithPedanticParse(true)`) enforces
    that nested-envelope JWTs declare `cty=JWT`. Tokens missing
    `cty` or carrying a different value are rejected. (#2094)

  * [jwt] `jwt.ParseInsecure` parses the loop-local payload split
    from the input rather than the original input bytes. Fixes a
    case where `ParseInsecure` could return a token assembled from
    a different signature segment than the one being inspected.
    (#2097)

  * [jwt] `jwt.WithMaxDeltaIs(...)` and `jwt.WithMinDeltaIs(...)`
    reject tokens whose compared claim is missing rather than
    treating it as zero. Validation no longer silently succeeds
    when the claim isn't present on the token. (#2099)

  * [jwt] `jwt.Parse` / `jwt.ParseRequest` only call `ParseForm`
    when `WithFormKey` is supplied. Previously the form body could
    be consumed even when the caller did not opt in to form-source
    extraction. (#2058)

  * [jwt] Fix `AddressClaim.MarshalJSON` to handle non-printable
    bytes correctly. (#2056)

  * [jwa] Unify the signature, key-encryption, and
    content-encryption algorithm tables behind a single
    registration entry point. Extension algorithms register once
    rather than via three parallel APIs. (#2066)

  * [cmd/jwx] Warn before writing a private key to a TTY; reject
    `keysize <= 0` for `oct` key generation. (#2071)

v3.1.0 19 Apr 2026
  * [jwk] Add `jwk.WithRejectDuplicateKID(bool)` — when enabled, `jwk.Parse` /
    `jwk.ParseReader` / `jwk.ParseString` return an error if the input JWKS
    contains more than one key sharing the same non-empty `kid`. Usable as a
    `jwk.Configure()` global or a per-call override. Default behavior
    (first-match-wins) is unchanged.

  * [jwk] Add `jwk.WithMaxKeys(int)` — caps the number of keys accepted
    by `jwk.Parse` / `jwk.ParseReader` / `jwk.ParseString` in both the
    JSON `"keys"` array and the PEM/X.509 block stream (default 1000).
    Usable as a `jwk.Configure()` global or a per-call override.
    Replaces the hardcoded internal PEM cap of the same value, so the
    default behavior is unchanged. Backport of the v4 amplification
    cap that mirrors `jws.WithMaxSignatures` and
    `jwe.WithMaxRecipients`.

  * [jws] Add `jws.WithDetachedPayloadReader(io.Reader)` — a streaming
    variant of `jws.WithDetachedPayload([]byte)` that consumes the
    payload from an `io.Reader` instead of a byte slice, so the payload
    is never materialized in memory. It is a `jws.Sign()` / `jws.Verify()`
    option; the two remain the default entry points. Only HMAC/RSA/ECDSA
    algorithms are supported; EdDSA, custom-family algorithms, and
    algorithms registered via `jws.RegisterSigner()` / `jws.RegisterVerifier()`
    are rejected with a clear error pointing at `jws.WithDetachedPayload()`.
    On sign, multiple `jws.WithKey()` options combined with `jws.WithJSON()`
    produce a general-form multi-signature JWS (the payload is streamed
    once and fanned out to each signer). On verify, only single-signature
    JWS input is supported; `jws.WithKeySet()`, `jws.WithKeyProvider()`,
    and `jws.WithVerifyAuto()` are not accepted. (#1663)

  * [jws] Add `jws.Base64StreamEncoder` — the stream-capable extension
    of `jws.Base64Encoder`. The default encoder and `*base64.Encoding`
    values supplied via `jws.WithBase64Encoder()` are auto-wrapped, so
    typical callers see no change. Custom encoders only need to
    implement this additional interface if they want to be usable with
    `jws.WithDetachedPayloadReader()`. (#1663)

  * [jws] Fix `jws.Sign` with `WithDetachedPayload` + `WithJSON` to
    omit the `"payload"` member from the output per RFC 7515
    Appendix F. Previously the payload was still emitted, producing
    non-detached JSON. (#1663)

  * [jwk] BREAKING: `jwk.PublicSetOf` now returns an error when the input
    set contains a symmetric (oct) key. Previously, symmetric keys were
    silently passed through — which meant callers following the documented
    "publish my public JWKS" pattern could leak HMAC secret material.
    Callers who genuinely want the legacy pass-through behavior can opt in
    with `jwk.WithAllowSymmetric(true)`. The signature is now variadic
    (`PublicSetOf(v Set, options ...PublicSetOption)`), so existing call
    sites compile unchanged. The minor version is bumped from v3.0.x →
    v3.1.0 to reflect this deliberate behavior change.

    `jwk.PublicKeyOf` on a single symmetric key is unchanged — it still
    returns the key as-is, matching its documented behavior.

  * [jws][jwe][jwk] Replace intermediate map[string]any allocation in
    MarshalJSON with a pair-slice + sync.Pool pattern, matching the approach
    already used in jwt. Eliminates per-call map and key-slice allocations
    in the serialization hot path.

  * [jwt][jwe][jws][jwk] Fix inconsistent mutex locking across main data
    structures. Named getters on JWK key types, MarshalJSON on JWK keys,
    UnmarshalJSON on JWE headers, makePairs/MarshalJSON on JWT tokens,
    rawBuffer on JWS headers, and Set/Keys on jwk.Set were missing proper
    lock protection. Switch all mutex fields from *sync.RWMutex (pointer)
    to sync.RWMutex (value) so go vet -copylocks catches accidental copies,
    and convert affected value-receiver methods to pointer receivers.

  * [jwe] Add `WithMaxRecipients(int)` to reject JWE messages with more recipients
    than the configured limit. Default is 100. Can be set globally via
    `jwe.Settings()` or per-call in `jwe.Decrypt()` / `jwe.Parse()`. (#1633)

  * [jws] Add `WithMaxSignatures(int)` to reject JWS JSON-serialized messages with
    more signatures than the configured limit. Default is 100. Can be set globally
    via `jws.Settings()` or per-call in `jws.Parse()`. (#1636)

  * [jwk] The default HTTP client used by `jwk.Fetch()` and `jwk.Cache` now
    enforces a 30-second timeout, blocks HTTPS-to-HTTP redirect downgrades at
    every hop, and limits redirect chains to 5 hops. This mitigates SSRF via
    redirect chains and slowloris-style DoS from unresponsive JWKS endpoints.
    Callers who provide their own `http.Client` via `jwk.WithHTTPClient()` are
    not affected. (#1634, #1637, #1639, #1640)

  * [jwk] Add `jwk.DefaultHTTPClient()` which returns a new `*http.Client`
    configured with the library's default protections. Useful for restoring
    defaults after calling `jwk.Configure(jwk.WithHTTPClient(...))`. (#1638)

  * [jwk] `WithMaxFetchBodySize(int64)` can now be set globally via
    `jwk.Configure()` in addition to per-call. (#1631)

  * [jwk] Add `jwk.WrapHTTPClientDefaults()` to apply the library's default
    safety behaviors (timeout, redirect policy) to a caller-provided
    `*http.Client`. Existing client settings (Transport, Jar, etc.) are
    preserved; CheckRedirect is wrapped rather than overwritten.

  * [jwt] Add `jwt.WithStrictStringClaims(true)` option for `jwt.Parse()` and
    `jwt.ReadFile()` to reject JSON `null` for string registered claims
    (`iss`, `sub`, `jti`). By default, null is silently accepted as an empty
    string. (#1484)

  * [jwa] Add fully-specified EdDSA signature algorithms `Ed25519` and `Ed448` per
    RFC 9864. The polymorphic `EdDSA` algorithm is now marked as deprecated.
    New Go accessors: `jwa.EdDSAEd25519()` and `jwa.EdDSAEd448()` (function names
    are tentative and may change in future releases).
    Ed448 signing/verification requires `github.com/cloudflare/circl` because
    Go's standard library does not support Ed448. To avoid pulling in this
    extra dependency for all users, Ed448 support is provided as a separate
    module (`github.com/lestrrat-go/jwx-circl-ed448`). Import it for side
    effects to enable Ed448:
      import _ "github.com/lestrrat-go/jwx-circl-ed448"
    Without this import, Ed448 is registered as an algorithm identifier but
    will return an error at sign/verify time.

  * [jwk] Add `jwk/jwkunsafe` package with `NewKey(kty)` and `NewPublicKey(kty)`
    functions for creating empty, unpopulated JWK key objects. This is intended
    for extension module authors who need to register custom KeyImporter
    implementations for new key types.

  * [jws] Add `jws.RegisterAlgorithmForKeyType()` for external modules to register
    additional algorithm-to-key-type mappings.

  * [jws/jwsbb] Add `jwsbb.RegisterAlgorithm()` for external modules to
    register custom algorithm implementations (e.g. Ed448).

  * [jws] `jws.Verify()` and `jws.VerifyCompactFast()` now validate the "crit"
    (Critical) header parameter per RFC 7515 Section 4.1.11. Signatures with an
    empty "crit" array, standard JOSE header names in "crit", or "crit"-listed
    extensions not present in the protected header are now rejected. To disable
    this validation on a per-call basis, pass
    `jws.WithCritValidation(false)` to `jws.Verify()`.

  * [jwe] `jwe.Decrypt()` now validates the "crit" (Critical) header parameter
    per RFC 7516 Section 4.1.13, matching the jws behavior above. Messages with
    an empty "crit" array, standard JOSE header names in "crit", or "crit"-listed
    extensions not present in the protected header are now rejected. Declare
    extensions with `jwe.WithCritExtension()`, or disable validation on a
    per-call basis with `jwe.WithCritValidation(false)`. (#1735)

  * [jwk] BREAKING (extension modules): `jwk.RegisterKeyExporter` now takes a
    `jwk.KeyKind` instead of `jwa.KeyType`. Call sites migrate with
    `jwk.KeyKind(kty.String())`; for curve-specific exporters, use a compound
    identity like `jwk.KeyKind("OKP:Ed448")`. Only affects extension-module
    authors registering custom exporters; library users calling `jwk.Export`
    are unaffected.

  * [jwk] Fixed inverted rlocker condition in RSA key export. (#1576)

  * [jwe] Fixed X25519 ECDH-ES key agreement to include `apu` and `apv` parameters
    in the Concat KDF derivation, matching the ECDSA path. Previously these values
    were silently discarded during encryption, weakening the key derivation per
    RFC 7518 Section 4.6.2.

  * [jwe] POTENTIALLY BREAKING: `jwe.Decrypt()` now rejects PBES2 messages with
    a `p2c` (iteration count) below 1,000 by default. This prevents accepting
    tokens with trivially low iteration counts that eliminate PBKDF2 brute-force
    protection. To restore the previous behavior or adjust the threshold, call
    `jwe.Settings(jwe.WithMinPBES2Count(0))`.

  * [jwk] Fixed a deadlock in `jwk.Cache` that occurred when repeated `Refresh()` calls
    failed (e.g. HTTP 500 responses). Each failure killed an httprc worker goroutine,
    and after all workers were exhausted, subsequent `Refresh()` calls would block forever. (#1551)

  * [jws] Add `jws.RegisterAlgorithmForCurve()` for external modules to register
    algorithm-to-curve mappings. `jws.AlgorithmsForKey()` now filters results
    by the key's curve when applicable. (#1620)

  * [jwk] Add `jwk.WithMaxFetchBodySize()` option to limit the response body size
    when fetching remote JWK Sets via `jwk.Fetch()` and `jwk.Cache`. (#1622)

  * [jwe] Add `jwe.WithMaxPBES2Count()` and `jwe.WithMinPBES2Count()` as per-call
    options to `jwe.Decrypt()`, allowing callers to override the global PBES2
    iteration count limits on a per-decryption basis. (#1623)

  * [jwk] `jwk.X509CertChain()` now correctly returns `false` as the second return
    value when the certificate chain is nil. (#1624)

  * [jwk] Fixed a data race in the x509 decoder registry iteration. (#1625)

  * [jwk] `jwk.Parse()` now limits PEM input to 1,000 blocks maximum to prevent
    resource exhaustion from inputs containing thousands of small PEM blocks. (#1626)

  * [jwk] RSA JWK validation is now enforced consistently across JSON parse,
    JWKS parse, PEM/X.509 parse, and `jwk.Import()`. Keys with moduli smaller
    than 2048 bits or unsafe public exponents are now rejected by default.
    Compatibility knobs were added to `jwk.Configure()` via
    `jwk.WithMinRSAModulusBits(...)` and `jwk.WithMinRSAPublicExponent(...)`.

  * [jwt] `jwt.Validate()` now rejects negative durations passed to
    `jwt.WithAcceptableSkew()`. (#1627)

  * [jwt/openid] `openid.Birthdate` now accepts year `0000` as a valid value per
    the OpenID Connect Core specification. (#1628)

  * [jwk][jws][jwe] Generated `Set()` methods now deep-copy slice and byte-slice
    values so callers cannot mutate internal header or key state after setting
    a field. (#1659)

  * [jwk] When `UnmarshalJSON` fails on a private key (RSA, EC, OKP, Symmetric),
    all sensitive fields are now zeroed before the error is returned. This
    prevents leaking partial key material through half-constructed objects. (#1660)

v3.0.13 12 Jan 2026
  * [jwt] The `jwt.WithContext()` option is now properly being passed to `jws.Verify()` from
    `jwt.Parse()`.
  * [jwx] github.com/lestrrat-go/httprc/v3 has been upgraded to remove dependency on 
    github.com/lestrrat-go/option (v1)
  * [jwk] `jwk.Clone()` has been fixed to properly work with private fields.

v3.0.12 20 Oct 2025
  * [jwe] As part of the next change, now per-recipient headers that are empty
    are no longer serialized in flattened JSON serialization.

  * [jwe] Introduce `jwe.WithLegacyHeaderMerging(bool)` option to control header
    merging behavior in during JWE encryption. This only applies to flattened
    JSON serialization.

    Previously, when using flattened JSON serialization (i.e. you specified
    JSON serialization via `jwe.WithJSON()` and only supplied one key), per-recipient
    headers were merged into the protected headers during encryption, and then
    were left to be included in the final serialization as-is. This caused duplicate
    headers to be present in both the protected headers and the per-recipient headers.

    Since there may be users who rely on this behavior already, instead of changing the
    default behavior to fix this duplication, a new option to `jwe.Encrypt()` was added
    to allow clearing the per-recipient headers after merging to leave the `"headers"`
    field empty. This in effect makes the flattened JSON serialization more similar to
    the compact serialization, where there are no per-recipient headers present, and
    leaves the headers disjoint.
    
    Note that in compact mode, there are no per-recipient headers and thus the
    headers need to be merged regardless. In full JSON serialization, we never
    merge the headers, so it is left up to the user to keep the headers disjoint.

  * [jws] Calling the deprecated `jws.NewSigner()` function for the first time will cause
    legacy signers to be loaded automatically. Previously, you had to explicitly
    call `jws.Settings(jws.WithLegacySigners(true))` to enable legacy signers.

    We incorrectly assumed that users would not be using `jws.NewSigner()`, and thus
    disabled legacy signers by default. However, it turned out that some users
    were using `jws.NewSigner()` in their code, which lead to breakages in 
    existing code. In hindsight we should have known that any API made public before will
    be used by _somebody_.

    As a side effect, jws.Settings(jws.WithLegacySigners(...)) is now a no-op.

    However, please do note that jws.Signer (and similar) objects were always intended to be
    used for _registering_ new signing/verifying algorithms, and not for end users to actually
    use them directly. If you are using them for other purposes, please consider changing
    your code, as it is more than likely that we will somehow deprecate/remove/discouraged
    their use in the future.

v3.0.11 14 Sep 2025
  * [jwk] Add `(jwk.Cache).Shutdown()` method that delegates to the httprc controller
    object, to shutdown the cache.
  * [jwk] Change timing of `res.Body.Close()` call
  * [jwe] Previously, ecdh.PrivateKey/ecdh.PublicKey were not properly handled
    when used for encryption, which has been fixed.
  * [jws/jwsbb] (EXPERIMENTAL/BREAKS COMPATIBILITY) Convert most functions into
    thin wrappers around functions from github.com/lestrrat-go/dsig package.
    As a related change, HAMCHashFuncFor/RSAHashFuncFor/ECDSAHashFuncFor/RSAPSSOptions
    have been removed or unexported.
    Users of this module should be using jwsbb.Sign() and jwsbb.Verify() instead of
    algorithm specific jwsbb.SignRSA()/jwsbb.VerifyRSA() and such. If you feel the
    need to use these functions, you should use github.com/lestrrat-go/dsig directly.

v3.0.10 04 Aug 2025
  * [jws/jwsbb] Add `jwsbb.ErrHeaderNotFound()` to return the same error type as when
    a non-existent header is requested. via `HeaderGetXXX()` functions. Previously, this
    function was called `jwsbb.ErrFieldNotFound()`, but it was a misnomer.
  * [jws/jwsbb] Fix a bug where error return values from `HeaderGetXXX()` functions
    could not be matched against `jwsbb.ErrHeaderNotFound()` using `errors.Is()`.

v3.0.9 31 Jul 2025
  * [jws/jwsbb] `HeaderGetXXX()` functions now return errors when
    the requested header is not found, or if the value cannot be
    converted to the requested type.

  * [jwt] `(jwt.Token).Get` methods now return specific types of errors depending
    on if a) the specified claim was not present, or b) the specified claim could
    not be assigned to the destination variable.

    You can distinguish these by using `errors.Is` against `jwt.ClaimNotFoundError()`
    or `jwt.ClaimAssignmentFailedError()`

v3.0.8 27 Jun 2025
  * [jwe/jwebb] (EXPERIMENTAL) Add low-level functions for JWE operations.
  * [jws/jwsbb] (EXPERIMENTAL/BREAKS COMPATIBILITY) Add io.Reader parameter
    so your choice of source of randomness can be passed. Defaults to crypto/rand.Reader.
    Function signatures around jwsbb.Sign() now accept an addition `rr io.Reader`,
    which can be nil for 99% of use cases.
  * [jws/jwsbb] Add HeaderParse([]byte), where it is expected that the header
    is already in its base64 decoded format.
  * misc: replace `interface{}` with `any`

v3.0.7 16 Jun 2025
  * [jws/jwsbb] (EXPERIMENTAL) Add low-level fast access to JWS headers in compact
    serialization form.
  * [jws] Fix error reporting when no key matched for a signature.
  * [jws] Refactor jws signer setup.
    * Known algorithms are now implemented completely in the jws/jwsbb package.
    * VerifierFor and SignerFor now always succeed, and will also return a Signer2
      or Verifier2 that wraps the legacy Signer or Verifier if one is registered.

v3.0.6 13 Jun 2025
  * This release contains various performance improvements all over the code.
    No, this time for real. In particular, the most common case for signing
    a JWT with a key is approx 70% more efficient based on the number of allocations.

    Please read the entry for the (retracted) v3.0.4 for what else I have to
    say about performance improvements

  * [jwt] Added fast-path for token signing and verification. The fast path
    is triggered if you only pass `jwt.Sign()` and `jwt.Parse()` one options each
    (`jwt.WithKey()`), with no suboptions.

  * [jws] Major refactoring around basic operations:

    * How to work with Signer/Verifier have completely changed. Please take
      a look at examples/jws_custom_signer_verifier_example_test.go for how
      to do it the new way. The old way still works, but it WILL be removed
      when v4 arrives.
    * Related to the above, old code has been moved to `jws/legacy`.

    * A new package `jws/jwsbb` has been added. `bb` stands for building blocks.
      This package separates out the low-level JWS operations into its own
      package. So if you are looking for just the signing of a payload with
      a key, this is it.

      `jws/jwsbb` is currently considered to be EXPERIMENTAL.

v3.0.5 11 Jun 2025
  * Retract v3.0.4
  * Code for v3.0.3 is the same as v3.0.3

v3.0.4 09 Jun 2025
  * This release contains various performance improvements all over the code.

    Because of the direction that this library is taking, we have always been
    more focused on correctness and usability/flexibility over performance.

    It just so happens that I had a moment of inspiration and decided to see
    just how good our AI-based coding agents are in this sort of analysis-heavy tasks.

    Long story short, the AI was fairly good at identifying suspicious code with
    an okay accuracy, but completely failed to make any meaningful changes to the
    code in a way that both did not break the code _and_ improved performance.
    I am sure that they will get better in the near future, but for now,
    I had to do the changes myself. I should clarify to their defence that
    the AI was very helpful in writing cumbersome benchmark code for me.

    The end result is that we have anywhere from 10 to 30% performance improvements
    in various parts of the code that we touched, based on number of allocations.
    We believe that this would be a significant improvement for many users.

    For further improvements, we can see that there would be a clear benefit to
    writing optimized code path that is designed to serve the most common cases.
    For example, for the case of signing JWTs with a single key, we could provide
    a path that skips a lot of extra processing (we kind of did that in this change,
    but we _could_ go ever harder in this direction). However, it is a trade-off between
    maintainability and performance, and as I am currently the sole maintainer of
    this library for the time being, I only plan to pursue such a route where it
    requires minimal effort on my part.

    If you are interested in helping out in this area, I hereby thank you in advance.
    However, please be perfectly clear that unlike other types of changes, for performance
    related changes, the balance between the performance gains and maintainability is
    top priority. If you have good ideas and code, they will always be welcome, but
    please be prepared to justify your changes.

    Finally, thank you for using this library!

v3.0.3 06 Jun 2025
  * Update some dependencies
  * [jwe] Change some error messages to contain more context information

v3.0.2 03 Jun 2025
  * [transform] (EXPERIMENTAL) Add utility function `transform.AsMap` to convert a
    Mappable object to a map[string]interface{}. This is useful for converting
    objects such as `jws.Header`, `jwk.Key`, `jwt.Token`, etc. to a map that can
    be used with other libraries that expect a map.
  * [jwt] (EXPERIMENTAL) Added token filtering functionality through the TokenFilter interface. 
  * [jwt/openid] (EXPERIMENTAL) Added StandardClaimsFilter() for filtering standard OpenID claims.
  * [jws] (EXPERIMENTAL) Added header filtering functionality through the HeaderFilter interface.
  * [jwe] (EXPERIMENTAL) Added header filtering functionality through the HeaderFilter interface.
  * [jwk] (EXPERIMENTAL) Added key filtering functionality through the KeyFilter interface.
  * [jwk] `jwk.Export` previously did not recognize third-party objects that implemented `jwk.Key`,
    as it was detecting what to do by checking if the object was one of our own unexported
    types. This caused some problems for consumers of this library that wanted to extend the
    features of the keys.

    Now `jwk.Export` checks types against interface types such as `jwk.RSAPrivateKey`, `jwk.ECDSAPrivateKey`, etc.
    It also uses some reflect blackmagic to detect if the given object implements the `jwk.Key` interface
    via embedding, so you should be able to embed a `jwk.Key` to another object to act as if it
    is a legitimate `jwk.Key`, as far as `jwk.Export` is concerned.

v3.0.1 29 Apr 2025
  * [jwe] Fixed a long standing bug that could lead to degraded encryption or failure to
    decrypt JWE messages when a very specific combination of inputs were used for
    JWE operations.

    This problem only manifested itself when the following conditions in content encryption or decryption
    were met:
      - Content encryption was specified to use DIRECT mode.
      - Contentn encryption algorithm is specified as A256CBC_HS512
      - The key was erronously constructed with a 32-byte content encryption key (CEK)

    In this case, the user would be passing a mis-constructed key of 32-bytes instead
    of the intended 64-bytes. In all other cases, this construction would cause
    an error because `crypto/aes.NewCipher` would return an error when a key with length
    not matching 16, 24, and 32 bytes is used. However, due to use using a the provided
    32-bytes as half CEK and half the hash, the `crypto/aes.NewCipher` was passed
    a 16-byte key, which is fine for AES-128. So internally `crypto/aes.NewCipher` would
    choose to use AES-128 instead of AES-256, and happily continue. Note that no other
    key lengths such as 48 and 128 would have worked. It had to be exactly 32.

    This does indeed result in a downgraded encryption, but we believe it is unlikely that this would cause a problem in the real world,
    as you would have to very specifically choose to use DIRECT mode, choose
    the specific content encryption algorithm, AND also use the wrong key size of
    exactly 32 bytes.

    However, in abandunce of caution, we recommend that you upgrade to v3.0.1 or later,
    or v2.1.6 or later if you are still on v2 series.

  * [jws] Improve performance of jws.SplitCompact and jws.SplitCompactString
  * [jwe] Improve performance of jwe.Parse

v3.0.0 1 Apr 2025
  * Release initial v3.0.0 series. Code is identical to v3.0.0-beta2, except
    for minor documentation changes.

    Please note that v1 will no longer be maintained.

    Going forward v2 will receive security updates but will no longer receive
    feature updates. Users are encouraged to migrate to v3. There is no hard-set
    guarantee as to how long v2 will be supported, but if/when v4 comes out,
    v2 support will be terminated then.

v3.0.0-beta2 30 Mar 2025
  * [jwk] Fix a bug where `jwk.Set`'s `Keys()` method did not return the proper
    non-standard fields. (#1322)
  * [jws][jwt] Implement `WithBase64Encoder()` options to pass base64 encoders
    to use during signing/verifying signatures. This useful when the token
    provider generates JWTs that don't follow the specification and uses base64
    encoding other than raw url encoding (no padding), such as, apparently,
    AWS ALB. (#1324, #1328)

v3.0.0-beta1 15 Mar 2025
  * [jwt] Token validation no longer truncates time based fields by default.
    To restore old behavior, you can either change the global settings by
    calling `jwt.Settings(jwt.WithTruncation(time.Second))`, or you can
    change it by each invocation by using `jwt.Validate(..., jwt.WithTruncation(time.Second))`

v3.0.0-alpha3 13 Mar 2025
  * [jwk] Importing/Exporting from jwk.Key with P256/P386/P521 curves to
    ecdh.PrivateKey/ecdh.PublicKey should now work. Previously these keys were not properly
    recognized by the exporter/importer. Note that keys that use X25519 and P256/P384/P521
    behave differently: X25519 keys can only be exported to/imported from OKP keys,
    while P256/P384/P521 can be exported to either ecdsa or ecdh keys.

v3.0.0-alpha2 25 Feb 2025
  * Update to work with go1.24
  * Update tests to work with latest latchset/jose
  * Fix build pipeline to work with latest golangci-lint
  * Require go1.23

v3.0.0-alpha1 01 Nov 2024
  * Initial release of v3 line.
