Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

io.jsonwebtoken.CompressionCodec: Error reading configuration file #648

Closed
waynercheung opened this issue Feb 9, 2021 · 13 comments
Closed

Comments

@waynercheung
Copy link

Hi,
I found an error message when I use jjwt, how does this error happen?
It seems that it is not an exception, so I cannot catch it.

@lhazlewood
Copy link
Contributor

No code example? Test case? Sample application? It's impossible for us to help without more information.

Also, for future usability questions, please kindly read https://github.com/jwtk/jjwt#help-questions before opening a GitHub issue. Thanks!

@waynercheung
Copy link
Author

I know it is a weird question, after I reboot my application, there is no more errors.
I cannot reproduce it, but I don't know why it occurred...

@mrwilby
Copy link

mrwilby commented Aug 5, 2022

@lhazlewood we're seeing the same in our AWS API Gateway+Lambda logs. This happens sporadically, and since AWS is recycling the underlying lambda container instances behind the scenes it's very difficult for us to create a reproducer for this.

Our guess is that something is leaking file/stream/IO handles (at least the call stack would appear to suggest as much). It might be a good idea to carefully review the stream-handling and make sure all resources are correctly being closed/released

Here's an exert from one of our logs:

5686535 [main] WARN  com.company.flows.AccessTokenFlow - Rejecting request due to exception 
java.util.ServiceConfigurationError: io.jsonwebtoken.CompressionCodec: Error reading configuration file
at java.util.ServiceLoader.fail(ServiceLoader.java:232)
at java.util.ServiceLoader.parse(ServiceLoader.java:309)
at java.util.ServiceLoader.access$200(ServiceLoader.java:185)
at java.util.ServiceLoader$LazyIterator.hasNextService(ServiceLoader.java:357)
at java.util.ServiceLoader$LazyIterator.hasNext(ServiceLoader.java:393)
at java.util.ServiceLoader$1.hasNext(ServiceLoader.java:474)
at io.jsonwebtoken.impl.lang.Services.loadAll(Services.java:80)
at io.jsonwebtoken.impl.lang.Services.loadAll(Services.java:68)
at io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver.<init>(DefaultCompressionCodecResolver.java:60)
at io.jsonwebtoken.impl.DefaultJwtParser.<init>(DefaultJwtParser.java:72)
at sun.reflect.GeneratedConstructorAccessor23.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at io.jsonwebtoken.lang.Classes.newInstance(Classes.java:156)
at io.jsonwebtoken.lang.Classes.newInstance(Classes.java:136)
at io.jsonwebtoken.Jwts.parser(Jwts.java:121)
at com.company.core.security.token.jwt.JsonWebTokenParser.<init>(JsonWebTokenParser.java:48)
at com.company.core.security.token.jwt.JsonWebTokenFactory.parser(JsonWebTokenFactory.java:10)
at com.company.core.security.authorization.framework.bridge.AuthorizationBridge.extractAccessTokenExpiration(AuthorizationBridge.java:429)
at com.company.core.security.authorization.framework.bridge.AuthorizationBridge.serializeData(AuthorizationBridge.java:78)
at com.company.security.authz.apigateway.flows.AccessTokenFlow.handleRequest(AccessTokenFlow.java:76)
at com.company.security.authz.apigateway.flows.DefaultCustomAuthorizerFlowManager.handleRequest(DefaultCustomAuthorizerFlowManager.java:46)
at com.company.security.authz.apigateway.CustomAuthorizer.handleRequest(CustomAuthorizer.java:57)
at sun.reflect.GeneratedMethodAccessor27.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at lambdainternal.EventHandlerLoader$PojoMethodRequestHandler.handleRequest(EventHandlerLoader.java:263)
at lambdainternal.EventHandlerLoader$PojoHandlerAsStreamHandler.handleRequest(EventHandlerLoader.java:180)
at lambdainternal.EventHandlerLoader$2.call(EventHandlerLoader.java:903)
at lambdainternal.AWSLambda.startRuntime(AWSLambda.java:349)
at lambdainternal.AWSLambda.<clinit>(AWSLambda.java:70)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at lambdainternal.LambdaRTEntry.main(LambdaRTEntry.java:150)
Caused by: java.io.FileNotFoundException: /var/task/META-INF/services/io.jsonwebtoken.CompressionCodec (Too many open files)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at sun.net.www.protocol.file.FileURLConnection.connect(FileURLConnection.java:90)
at sun.net.www.protocol.file.FileURLConnection.getInputStream(FileURLConnection.java:188)
at java.net.URL.openStream(URL.java:1093)
at java.util.ServiceLoader.parse(ServiceLoader.java:304)
... 33 common frames omitted
5686536 [main] WARN  com.company.a.e.ApiGatewayException - Request rejected due to Unauthorized: Exception during request processing 
Unauthorized: com.company.security.authz.aws.apigateway.exceptions.ApiGatewayException
com.company.security.authz.aws.apigateway.exceptions.ApiGatewayException: Unauthorized
	at com.company.security.authz.aws.apigateway.exceptions.ApiGatewayException.newUnauthorized(ApiGatewayException.java:20)
	at com.company.security.authz.apigateway.flows.AccessTokenFlow.handleRequest(AccessTokenFlow.java:84)
	at com.company.security.authz.apigateway.flows.DefaultCustomAuthorizerFlowManager.handleRequest(DefaultCustomAuthorizerFlowManager.java:46)
	at com.company.security.authz.apigateway.CustomAuthorizer.handleRequest(CustomAuthorizer.java:57)
	at sun.reflect.GeneratedMethodAccessor27.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)

END RequestId: c1c9a7f8-225f-42df-83f6-2716e310a045
REPORT RequestId: c1c9a7f8-225f-42df-83f6-2716e310a045	Duration: 106.60 ms	Billed Duration: 107 ms	Memory Size: 3008 MB	Max Memory Used: 677 MB	

Is it possible to reopen this ticket?

@lhazlewood
Copy link
Contributor

@mrwilby - thanks for the stack trace, that was helpful!

It's so weird that you posted this today - another problem with ServiceLoader was discussed today: #751

This is due to the way JJWT uses the JDK ServiceLoader to look up pluggable implementations: ServiceLoader looks for a file in META-INF that contains a fully qualified class name of the implementation class to instantiate. The "too many open files" message in the stack trace sounds like Lambda's file I/O is having problems when accessing that file within the dependency .jar.

So just like in #751, the workaround for this (until we can address the ServiceLoader calls) is to instantiate and configure a CompressionCodec instance directly on the JwtBuilder and/orJwtParser (in your case, aCompressionCodecResolverthat is used at parse time to return the correspondingCompressionCodec` instance as documented here: https://github.com/jwtk/jjwt/tree/0.10.7#compression-custom).

The logic that calls ServiceLoader is only invoked if the respective component is not explicitly configured. So if you explicitly configure an instance, ServiceLoader is never called. Please see #751 and the discussion there.

I'll consider this issue a duplicate of that one, so I don't think it needs be re-opened, unless I'm missing something.

cc @bdemers

@mrwilby
Copy link

mrwilby commented Aug 5, 2022

Thanks @lhazlewood . We're not actually using compression in our system, so we'll possibly disable that as you suggested.

However, I'm still not clear on what is actually responsible for the handle leak. Do you believe that the root cause of the problem is somehow related to the ServiceLoader calls themselves?

@lhazlewood
Copy link
Contributor

@mrwilby it most definitely is. The JDK ServiceLoader implementation will look for a file in any .jar's META-INF/services/ directory for a file with the same name as the interface (e.g. com.whatever.my.Interface). It will then read that file's first line to determine the fully qualified class name of the implementation of that interface. It will finally create an instance of that implementation fqcn and return it.

This is corroborated by your stack trace's line:

Caused by: java.io.FileNotFoundException: /var/task/META-INF/services/io.jsonwebtoken.CompressionCodec (Too many open files)

and more directly, it's the ServiceLoader implementation at the top of the stack:

at java.util.ServiceLoader.fail(ServiceLoader.java:232)
at java.util.ServiceLoader.parse(ServiceLoader.java:309)
at java.util.ServiceLoader.access$200(ServiceLoader.java:185)
at java.util.ServiceLoader$LazyIterator.hasNextService(ServiceLoader.java:357)
at java.util.ServiceLoader$LazyIterator.hasNext(ServiceLoader.java:393)
at java.util.ServiceLoader$1.hasNext(ServiceLoader.java:474)
at io.jsonwebtoken.impl.lang.Services.loadAll(Services.java:80)

So as you can see, JJWT's Services class calls the JDK's ServiceLoader API, and then it fails from within there. Since we didn't write that code, we can't do anything about it other than suggest the aforementioned workaround and/or change our implementation to not use the JDK ServiceLoader on a future release.

I hope that helps!

cc @bdemers

@lhazlewood
Copy link
Contributor

Based on recent discussion here (thank you @mrwilby ! ), this issue has been superseded by #752.

@lyz-tech
Copy link

lyz-tech commented Aug 16, 2022

i have the same problem twice a week, after I reboot my application, there is no more errors.

@bdemers
Copy link
Member

bdemers commented Aug 16, 2022

Which versions of Java are you folks using?

@mrwilby
Copy link

mrwilby commented Aug 16, 2022

We're using Open JDK 8 (I can check the specific version if it helps).
However, just to muddy the water a little, we did find a potential handle leak in our own code. This makes me wonder whether the leak is outside of the JJWT library and that it just happens to manifest when some other handle allocation is attempted (which fails due to exhaustion).

@bdemers
Copy link
Member

bdemers commented Aug 16, 2022

That would be my guess, but it does seem odd that multiple people have reported it.
It's possible both were on the edge, and then the ServiceLoader usage pushed it over.

@mrwilby did fixing your handle leak resolve this issue for you?

@lhazlewood
Copy link
Contributor

There could be a file handle leak in application code, sure, but it could also just be the volume of calls to ServiceLoader.

I personally would expect ServiceLoader to cache fqcn lookup results so it doesn't have to re-read files again for the same fqcn, but I don't know if it does for sure. If it does not, and enough calls to create a JWT parser exist (e.g. creating a JwtParser on every http request, instead of creating a JwtParser application singleton, which looks to be the case in the above stack trace), then hundreds or thousands of file I/O attempts could occur over the lifespan of the application (two or three per JwtParser creation). If that's the case, and the file handles are not closed quickly enough (e.g. cached or scheduled-but-not-closed by the operating system), Amazon's ulimit settings just may not be high enough.

#752 will eliminate any file I/O no matter how many (or not) parsers are created, so that should put an end to these issues in these types of transient environments, I would think.

@bdemers
Copy link
Member

bdemers commented Aug 17, 2022

OH! I would have assumed it would cache the lookup too (I'll try to debug a little to confirm). We could layer in a cache, instead of migrating away #752, but we continue the discussion over on that issue after a little more digging.

0utplay referenced this issue in CloudNetService/CloudNet May 18, 2024
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [io.jsonwebtoken:jjwt-gson](https://togithub.com/jwtk/jjwt) | `0.11.5`
-> `0.12.5` |
[![age](https://developer.mend.io/api/mc/badges/age/maven/io.jsonwebtoken:jjwt-gson/0.12.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/maven/io.jsonwebtoken:jjwt-gson/0.12.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/maven/io.jsonwebtoken:jjwt-gson/0.11.5/0.12.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/io.jsonwebtoken:jjwt-gson/0.11.5/0.12.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| [io.jsonwebtoken:jjwt-impl](https://togithub.com/jwtk/jjwt) | `0.11.5`
-> `0.12.5` |
[![age](https://developer.mend.io/api/mc/badges/age/maven/io.jsonwebtoken:jjwt-impl/0.12.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/maven/io.jsonwebtoken:jjwt-impl/0.12.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/maven/io.jsonwebtoken:jjwt-impl/0.11.5/0.12.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/io.jsonwebtoken:jjwt-impl/0.11.5/0.12.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
| [io.jsonwebtoken:jjwt-api](https://togithub.com/jwtk/jjwt) | `0.11.5`
-> `0.12.5` |
[![age](https://developer.mend.io/api/mc/badges/age/maven/io.jsonwebtoken:jjwt-api/0.12.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/maven/io.jsonwebtoken:jjwt-api/0.12.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/maven/io.jsonwebtoken:jjwt-api/0.11.5/0.12.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/io.jsonwebtoken:jjwt-api/0.11.5/0.12.5?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>jwtk/jjwt (io.jsonwebtoken:jjwt-gson)</summary>

###
[`v0.12.5`](https://togithub.com/jwtk/jjwt/blob/HEAD/CHANGELOG.md#0125)

[Compare Source](https://togithub.com/jwtk/jjwt/compare/0.12.4...0.12.5)

This patch release:

- Ensures that builders' `NestedCollection` changes are applied to the
collection immediately as mutation methods are called, no longer
requiring application developers to call `.and()` to 'commit' or apply a
change. For example, prior to this release,
    the following code did not apply changes:

    ```java
    JwtBuilder builder = Jwts.builder();
    builder.audience().add("an-audience"); // no .and() call
    builder.compact(); // would not keep 'an-audience'
    ```

Now this code works as expected and all other `NestedCollection`
instances like it apply changes immediately (e.g. when calling
    `.add(value)`).

However, standard fluent builder chains are still recommended for
readability when feasible, e.g.

    ```java
    Jwts.builder()
        .audience().add("an-audience").and() // allows fluent chaining
        .subject("Joe")
        // etc...
        .compact()
    ```

    See [Issue 916](https://togithub.com/jwtk/jjwt/issues/916).

###
[`v0.12.4`](https://togithub.com/jwtk/jjwt/blob/HEAD/CHANGELOG.md#0124)

[Compare Source](https://togithub.com/jwtk/jjwt/compare/0.12.3...0.12.4)

This patch release includes various changes listed below.

##### Jackson Default Parsing Behavior

This release makes two behavioral changes to JJWT's default Jackson
`ObjectMapper` parsing settings:

1. In the interest of having stronger standards to reject potentially
malformed/malicious/accidental JSON that could
have undesirable effects on an application, JJWT's default `
ObjectMapper `is now configured to explicitly reject/fail
parsing JSON (JWT headers and/or Claims) if/when that JSON contains
duplicate JSON member names.

For example, now the following JSON, if parsed, would fail (be rejected)
by default:

    ```json
    {
      "hello": "world",
      "thisWillFail": 42,
      "thisWillFail": "test"
    }
    ```

Technically, the JWT RFCs *do allow* duplicate named fields as long as
the last parsed member is the one used
(see [JWS RFC 7515, Section
4](https://datatracker.ietf.org/doc/html/rfc7515#section-4)), so this is
allowed.
However, because JWTs often reflect security concepts, it's usually
better to be defensive and reject these
unexpected scenarios by default. The RFC later supports this
position/preference in
[Section
10.12](https://datatracker.ietf.org/doc/html/rfc7515#section-10.12):

        Ambiguous and potentially exploitable situations
could arise if the JSON parser used does not enforce the uniqueness
        of member names or returns an unpredictable value for duplicate
        member names.

Finally, this is just a default, and the RFC does indeed allow duplicate
member names if the last value is used,
so applications that require duplicates to be allowed can simply
configure their own `ObjectMapper` and use
    that with JJWT instead of assuming this (new) JJWT default. See
[Issue #&#8203;877](https://togithub.com/jwtk/jjwt/issues/877) for more.
2.  If using JJWT's support to use Jackson to parse
[Custom Claim
Types](https://togithub.com/jwtk/jjwt#json-jackson-custom-types) (for
example, a Claim that should be
unmarshalled into a POJO), and the JSON for that POJO contained a member
that is not represented in the specified
class, Jackson would fail parsing by default. Because POJOs and JSON
data models can sometimes be out of sync
due to different class versions, the default behavior has been changed
to ignore these unknown JSON members instead
of failing (i.e. the `ObjectMapper`'s
`DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES` is now set to
`false`)
    by default.

Again, if you prefer the stricter behavior of rejecting JSON with extra
or unknown properties, you can configure
`true` on your own `ObjectMapper` instance and use that instance with
the `Jwts.parser()` builder.

##### Additional Changes

This release also:

- Fixes a thread-safety issue when using `java.util.ServiceLoader` to
dynamically lookup/instantiate pluggable
    implementations of JJWT interfaces (e.g. JSON parsers, etc).  See
[Issue #&#8203;873](https://togithub.com/jwtk/jjwt/issues/873) and its
documented fix in
    [PR #&#8203;893](https://togithub.com/jwtk/jjwt/pull/892).
- Ensures Android environments and older `org.json` library usages can
parse JSON from a `JwtBuilder`-provided
`java.io.Reader` instance. [Issue
882](https://togithub.com/jwtk/jjwt/issues/882).
- Ensures a single string `aud` (Audience) claim is retained (without
converting it to a `Set`) when copying/applying a
source Claims instance to a destination Claims builder. [Issue
890](https://togithub.com/jwtk/jjwt/issues/890).
- Ensures P-256, P-384 and P-521 Elliptic Curve JWKs zero-pad their
field element (`x`, `y`, and `d`) byte array values
if necessary before Base64Url-encoding per [RFC
7518](https://datatracker.ietf.org/doc/html/rfc7518), Sections

[6.2.1.2](https://datatracker.ietf.org/doc/html/rfc7518#section-6.2.1.2),

[6.2.1.3](https://datatracker.ietf.org/doc/html/rfc7518#section-6.2.1.3),
and

[6.2.2.1](https://datatracker.ietf.org/doc/html/rfc7518#section-6.2.2.1),
respectively.
    [Issue 901](https://togithub.com/jwtk/jjwt/issues/901).
- Ensures that Secret JWKs for HMAC-SHA algorithms with `k` sizes larger
than the algorithm minimum can
be parsed/used as expected. See [Issue
#&#8203;905](https://togithub.com/jwtk/jjwt/issues/905)
- Ensures there is an upper bound (maximum) iterations enforced for
PBES2 decryption to help mitigate potential DoS
attacks. Many thanks to Jingcheng Yang and Jianjun Chen from Sichuan
University and Zhongguancun Lab for their
    work on this. See [PR 911](https://togithub.com/jwtk/jjwt/pull/911).
- Fixes various typos in documentation and JavaDoc. Thanks to those
contributing pull requests for these!

###
[`v0.12.3`](https://togithub.com/jwtk/jjwt/blob/HEAD/CHANGELOG.md#0123)

[Compare Source](https://togithub.com/jwtk/jjwt/compare/0.12.2...0.12.3)

This patch release:

- Upgrades the `org.json` dependency to `20231013` to address that
library's
[CVE-2023-5072](https://nvd.nist.gov/vuln/detail/CVE-2023-5072)
vulnerability.
- (Re-)enables empty values for custom claims, which was the behavior in
<= 0.11.5.
    [Issue 858](https://togithub.com/jwtk/jjwt/issues/858).

###
[`v0.12.2`](https://togithub.com/jwtk/jjwt/blob/HEAD/CHANGELOG.md#0122)

[Compare Source](https://togithub.com/jwtk/jjwt/compare/0.12.1...0.12.2)

This is a follow-up release to finalize the work in 0.12.1 that tried to
fix a reflection scope problem
on >= JDK 17. The 0.12.1 fix worked, but only if the importing project
or application did *not* have its own
`module-info.java` file.

This release removes that reflection code entirely in favor of a
JJWT-native implementation, eliminating JPMS
module (scope) problems on >= JDK 17. As such, `--add-opens` flags are
no longer required to use JJWT.

The fix has been tested up through JDK 21 in a separate application
environment (out of JJWT's codebase) to assert
expected functionality in a 'clean room' environment in a project both
with and without `module-info.java` usage.

###
[`v0.12.1`](https://togithub.com/jwtk/jjwt/blob/HEAD/CHANGELOG.md#0121)

[Compare Source](https://togithub.com/jwtk/jjwt/compare/0.12.0...0.12.1)

Enabled reflective access on JDK 17+ to `java.io.ByteArrayInputStream`
and `sun.security.util.KeyUtil` for
`jjwt-impl.jar`

###
[`v0.12.0`](https://togithub.com/jwtk/jjwt/blob/HEAD/CHANGELOG.md#0120)

[Compare Source](https://togithub.com/jwtk/jjwt/compare/0.11.5...0.12.0)

This is a big release! JJWT now fully supports Encrypted JSON Web Tokens
(JWE), JSON Web Keys (JWK) and more! See the
sections below enumerating all new features as well as important notes
on breaking changes or backwards-incompatible
changes made in preparation for the upcoming 1.0 release.

**Because breaking changes are being introduced, it is strongly
recommended to wait until the upcoming 1.0 release
where you can address breaking changes one time only**.

Those that need immediate JWE encryption and JWK key support
however will likely want to upgrade now and deal with the smaller subset
of breaking changes in the 1.0 release.

##### Simplified Starter Jar

Those upgrading to new modular JJWT versions from old single-jar
versions will transparently obtain everything
they need in their Maven, Gradle or Android projects.

JJWT's early releases had one and only one .jar: `jjwt.jar`. Later
releases moved to a modular design with 'api' and
'impl' jars including 'plugin' jars for Jackson, GSON, org.json, etc.
Some users upgrading from the earlier single
jar to JJWT's later versions have been frustrated by being forced to
learn how to configure the more modular .jars.

This release re-introduces the `jjwt.jar` artifact again, but this time
it is simply an empty .jar with Maven
metadata that will automatically transitively download the following
into a project, retaining the old single-jar
behavior:

-   `jjwt-api.jar`
-   `jjwt-impl.jar`
-   `jjwt-jackson.jar`

Naturally, developers are still encouraged to configure the modular
.jars as described in JJWT's documentation for
greater control and to enable their preferred JSON parser, but this
stop-gap should help those unaware when upgrading.

##### JSON Web Encryption (JWE) Support!

This has been a long-awaited feature for JJWT, years in the making, and
it is quite extensive - so many encryption
algorithms and key management algorithms are defined by the JWA
specification, and new API concepts had to be
introduced for all of them, as well as extensive testing with
RFC-defined test vectors. The wait is over!\
All JWA-defined encryption algorithms and key management algorithms are
fully implemented and supported and
available immediately.  For example:

```java
AeadAlgorithm enc = Jwts.ENC.A256GCM;
SecretKey key = enc.key().build();
String compact = Jwts.builder().setSubject("Joe").encryptWith(key, enc).compact();

Jwe<Claims> jwe = Jwts.parser().decryptWith(key).build().parseEncryptedClaims(compact);
```

Many other RSA and Elliptic Curve examples are in the full README
documentation.

##### JSON Web Key (JWK) Support!

Representing cryptographic keys - SecretKeys, RSA Public and Private
Keys, Elliptic Curve Public and
Private keys - as fully encoded JSON objects according to the JWK
specification - is now fully implemented and
supported. The new `Jwks` utility class exists to create JWK builders
and parsers as desired. For example:

```java
SecretKey key = Jwts.SIG.HS256.key().build();
SecretJwk jwk = Jwks.builder().forKey(key).build();
assert key.equals(jwk.toKey());

// or if receiving a JWK string:
Jwk<?> parsedJwk = Jwks.parser().build().parse(jwkString);
assert jwk.equals(parsedJwk);
assert key.equals(parsedJwk.toKey());
```

Many JJWT users won't need to use JWKs explicitly, but some JWA Key
Management Algorithms (and lots of RFC test
vectors) utilize JWKs when transmitting JWEs. As this was required by
JWE, it is now implemented in full for
JWE use as well as general-purpose JWK support.

##### JWK Thumbprint and JWK Thumbprint URI support

The [JWK Thumbprint](https://www.rfc-editor.org/rfc/rfc7638.html) and
[JWK Thumbprint URI](https://www.rfc-editor.org/rfc/rfc9278.html) RFC
specifications are now fully supported. Please
see the README.md file's corresponding named sections for both for full
documentation and usage examples.

##### JWS Unencoded Payload Option (`b64`) support

The [JSON Web Signature (JWS) Unencoded Payload
Option](https://www.rfc-editor.org/rfc/rfc7797.html) RFC specification
is now fully supported. Please see the README.md corresponding named
section for documentation and usage examples.

##### Better PKCS11 and Hardware Security Module (HSM) support

Previous versions of JJWT enforced that Private Keys implemented the
`RSAKey` and `ECKey` interfaces to enforce key
length requirements. With this release, JJWT will still perform those
checks when those data types are available,
but if not, as is common with keys from PKCS11 and HSM KeyStores, JJWT
will still allow those Keys to be used,
expecting the underlying Security Provider to enforce any key
requirements. This should reduce or eliminate any
custom code previously written to extend JJWT to use keys from those
KeyStores or Providers.

Additionally, PKCS11/HSM tests using
[SoftHSMv2](https://www.opendnssec.org/softhsm/) are run on every build
with
every JWS MAC and Signature algorithm and every JWE Key algorithm to
ensure continued stable support with
Android and Sun PKCS11 implementations and spec-compliant Hardware
Security Modules that use the PKCS11 interface
(such as YubiKey, etc.)

##### Custom Signature Algorithms

The `io.jsonwebtoken.SignatureAlgorithm` enum has been deprecated in
favor of new
`io.jsonwebtoken.security.SecureDigestAlgorithm`,
`io.jsonwebtoken.security.MacAlgorithm`, and
`io.jsonwebtoken.security.SignatureAlgorithm` interfaces to allow custom
algorithm implementations. The new nested
`Jwts.SIG` static inner class is a registry of all standard JWS
algorithms as expected, exactly like the
old enum. This change was made because enums are a static concept by
design and cannot
support custom values: those who wanted to use custom signature
algorithms could not do so until now. The new
interfaces now allow anyone to plug in and support custom algorithms
with JJWT as desired.

##### KeyBuilder and KeyPairBuilder

Because the `io.jsonwebtoken.security.Keys#secretKeyFor` and
`io.jsonwebtoken.security.Keys#keyPairFor` methods
accepted the now-deprecated `io.jsonwebtoken.SignatureAlgorithm` enum,
they have also been deprecated in favor of
calling new `key()` or `keyPair()` builder methods on `MacAlgorithm` and
`SignatureAlgorithm` instances directly.\
For example:

```java
SecretKey key = Jwts.SIG.HS256.key().build();
KeyPair pair = Jwts.SIG.RS256.keyPair().build();
```

The builders allow for customization of the JCA `Provider` and
`SecureRandom` during Key or KeyPair generation if desired, whereas
the old enum-based static utility methods did not.

##### Preparation for 1.0

Now that the JWE and JWK specifications are implemented, only a few
things remain for JJWT to be considered at
version 1.0. We have been waiting to apply the 1.0 release version
number until the entire set of JWT specifications
are fully supported **and** we drop JDK 7 support (to allow users to use
JDK 8 APIs). To that end, we have had to
deprecate some concepts, or in some cases, completely break backwards
compatibility to ensure the transition to
1.0 (and JDK 8 APIs) are possible. Most backwards-incompatible changes
are listed in the next section below.

##### Backwards Compatibility Breaking Changes, Warnings and
Deprecations

- `io.jsonwebtoken.Jwt`'s `getBody()` method has been deprecated in
favor of a new `getPayload()` method to
    reflect correct JWT specification nomenclature/taxonomy.

- `io.jsonwebtoken.Jws`'s `getSignature()` method has been deprecated in
favor of a new `getDigest()` method to
support expected congruent behavior with `Jwe` instances (both have
digests).

- `io.jsonwebtoken.JwtParser`'s `parseContentJwt`, `parseClaimsJwt`,
`parseContentJws`, and `parseClaimsJws` methods
have been deprecated in favor of more intuitive respective
`parseUnsecuredContent`, `parseUnsecuredClaims`,
    `parseSignedContent` and `parseSignedClaims` methods.

- `io.jsonwebtoken.CompressionCodec` is now deprecated in favor of the
new `io.jsonwebtoken.io.CompressionAlgorithm`
interface. This is to guarantee API congruence with all other
JWT-identifiable algorithm IDs that can be set as a
    header value.

- `io.jsonwebtoken.CompressionCodecResolver` has been deprecated in
favor of the new
    `JwtParserBuilder#addCompressionAlgorithms` method.

##### Breaking Changes

- **`io.jsonwebtoken.Claims` and `io.jsonwebtoken.Header` instances are
now immutable** to enhance security and thread
safety. Creation and mutation are supported with newly introduced
`ClaimsBuilder` and `HeaderBuilder` concepts.
Even though mutation methods have migrated, there are a couple that have
been removed entirely:
- `io.jsonwebtoken.JwsHeader#setAlgorithm` has been removed - the
`JwtBuilder` will always set the appropriate
        `alg` header automatically based on builder state.
- `io.jsonwebtoken.Header#setCompressionAlgorithm` has been removed -
the `JwtBuilder` will always set the appropriate
        `zip` header automatically based on builder state.

- `io.jsonwebtoken.Jwts`'s `header(Map)`, `jwsHeader()` and
`jwsHeader(Map)` methods have been removed in favor
of the new `header()` method that returns a `HeaderBuilder` to support
method chaining and dynamic `Header` type
creation. The `HeaderBuilder` will dynamically create a `Header`,
`JwsHeader` or `JweHeader` automatically based on
    builder state.

- Similarly, `io.jsonwebtoken.Jwts`'s `claims()` static method has been
changed to return a `ClaimsBuilder` instead
    of a `Claims` instance.

- **JWTs that do not contain JSON Claims now have a payload type of
`byte[]` instead of `String`** (that is,
`Jwt<byte[]>` instead of `Jwt<String>`). This is because JWTs,
especially when used with the
`cty` (Content Type) header, are capable of handling *any* type of
payload, not just Strings. The previous JJWT
releases didn't account for this, and now the API accurately reflects
the JWT RFC specification payload
capabilities. Additionally, the name of `plaintext` has been changed to
`content` in method names and JavaDoc to
reflect this taxonomy. This change has impacted the following JJWT APIs:

- The `JwtBuilder`'s `setPayload(String)` method has been deprecated in
favor of two new methods:

        -   `setContent(byte[])`, and
        -   `setContent(byte[], String contentType)`

        These new methods allow any kind of content
within a JWT, not just Strings. The existing `setPayload(String)` method
implementation has been changed to
delegate to this new `setContent(byte[])` method with the argument's
UTF-8 bytes, for example
        `setContent(payloadString.getBytes(StandardCharsets.UTF_8))`.

- The `JwtParser`'s `Jwt<Header, String> parsePlaintextJwt(String
plaintextJwt)` and
`Jws<String> parsePlaintextJws(String plaintextJws)` methods have been
changed to
        `Jwt<Header, byte[]> parseContentJwt(String plaintextJwt)` and
        `Jws<byte[]> parseContentJws(String plaintextJws)` respectively.

- `JwtHandler`'s `onPlaintextJwt(String)` and `onPlaintextJws(String)`
methods have been changed to
        `onContentJwt(byte[])` and `onContentJws(byte[])` respectively.

- `io.jsonwebtoken.JwtHandlerAdapter` has been changed to reflect the
above-mentioned name and `String`-to-`byte[]`
argument changes, as well adding the `abstract` modifier. This class was
never intended
to be instantiated directly, and is provided for subclassing only. The
missing modifier has been added to ensure
        the class is used as it had always been intended.

- `io.jsonwebtoken.SigningKeyResolver`'s `resolveSigningKey(JwsHeader,
String)` method has been changed to
        `resolveSigningKey(JwsHeader, byte[])`.

- `io.jsonwebtoken.JwtParser` is now immutable. All
mutation/modification methods (setters, etc) deprecated 4 years
ago have been removed. All parser configuration requires using the
`JwtParserBuilder`.

- Similarly, `io.jsonwebtoken.Jwts`'s `parser()` method deprecated 4
years ago has been changed to now return a
`JwtParserBuilder` instead of a direct `JwtParser` instance. The
previous `Jwts.parserBuilder()` method has been
    removed as it is now redundant.

- The `JwtParserBuilder` no longer supports `PrivateKey`s for signature
verification. This was an old
legacy behavior scheduled for removal years ago, and that change is now
complete. For various cryptographic/security
reasons, asymmetric public/private key signatures should always be
created with `PrivateKey`s and verified with
    `PublicKey`s.

- `io.jsonwebtoken.CompressionCodec` implementations are no longer
discoverable via `java.util.ServiceLoader` due to
runtime performance problems with the JDK's `ServiceLoader`
implementation per

[https://github.com/jwtk/jjwt/issues/648](https://togithub.com/jwtk/jjwt/issues/648)/648.
Custom implementations should be made available to the `JwtParser` via
    the new `JwtParserBuilder#addCompressionAlgorithms` method.

- Prior to this release, if there was a serialization problem when
serializing the JWT Header, an `IllegalStateException`
was thrown. If there was a problem when serializing the JWT claims, an
`IllegalArgumentException` was
thrown. This has been changed up to ensure consistency: any
serialization error with either headers or claims
    will now throw a `io.jsonwebtoken.io.SerializationException`.

- Parsing of unsecured JWTs (`alg` header of `none`) are now disabled by
default as mandated by
[RFC 7518, Section
3.6](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.6). If you
require parsing of
unsecured JWTs, you must call the `JwtParserBuilder#enableUnsecured()`
method, but note the security
    implications mentioned in that method's JavaDoc before doing so.

- `io.jsonwebtoken.gson.io.GsonSerializer` now requires `Gson` instances
that have a registered
    `GsonSupplierSerializer` type adapter, for example:
    ```java
    new GsonBuilder()
.registerTypeHierarchyAdapter(io.jsonwebtoken.lang.Supplier.class,
GsonSupplierSerializer.INSTANCE)
      .disableHtmlEscaping().create();
    ```
This is to ensure JWKs have `toString()` and application log safety (do
not print secure material), but still
    serialize to JSON correctly.

- `io.jsonwebtoken.InvalidClaimException` and it's two subclasses
(`IncorrectClaimException` and `MissingClaimException`)
were previously mutable, allowing the corresponding claim name and claim
value to be set on the exception after
creation. These should have always been immutable without those setters
(just getters), and this was a previous
implementation oversight. This release has ensured they are immutable
without the setters.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 6:00am" in timezone
Europe/Berlin, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these
updates again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/CloudNetService/CloudNet).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zNjMuNSIsInVwZGF0ZWRJblZlciI6IjM3LjM2My41IiwidGFyZ2V0QnJhbmNoIjoibmlnaHRseSIsImxhYmVscyI6WyJ0OiBkZXBlbmRlbmNpZXMiXX0=-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: 0utplay <aldin@sijamhodzic.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants