com.nimbusds:nimbus-jose-jwt is the most widely deployed JOSE and JWT library on the JVM, sitting underneath Spring Security's OAuth support and countless identity products, and nearly every real-world vulnerability involving it comes from how applications call it, not from the cryptography inside it. Signature verification skipped, algorithms accepted from attacker input, claims never validated: these are integration bugs, and the library gives you the tools to prevent all of them. This guide covers the dependency itself, its actual CVE record (shorter and more interesting than scanners suggest), and the verification pattern that should be your default.
The dependency and where it comes from
The nimbus-jose-jwt maven declaration:
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>10.3</version>
</dependency>
The library is developed by Connect2id (the company behind a commercial OpenID Connect server) under the Apache 2.0 license, with an unusually brisk release cadence: the 10.x line opened in January 2025, with 10.3 arriving in May 2025. It implements the full JOSE stack, JWS, JWE, JWK, JWT, not just token parsing, which is why identity vendors and frameworks standardize on it.
You often have it without declaring it. Spring Security's spring-security-oauth2-jose module, many API gateways, and most Java OIDC client libraries bring it transitively, so check mvn dependency:tree -Dincludes=com.nimbusds before assuming you are not affected by anything below.
The CVE record, including the ghost findings
The library's direct advisory history is short. The notable entry is CVE-2023-52428: before version 9.37.2, a JWE object using password-based encryption could declare an enormous p2c value (the PBKDF2 iteration count) in its header, and the PasswordBasedDecrypter would obligingly attempt that many iterations, burning CPU until the service starved. CVSS 7.5, network-exploitable, no authentication required. The fix caps the iteration count. If you process JWE from untrusted parties on a version below 9.37.2, upgrade; that is the whole remediation.
The more common scanner story is different: for years, findings against nimbus-jose-jwt were actually findings against json-smart, its former JSON parser dependency, which accumulated its own DoS CVEs. The maintainers resolved this structurally in version 9.24 by dropping json-smart entirely and shading in Gson 2.9.1. Two consequences worth writing into your triage notes:
- Anything at 9.24+ no longer has the json-smart exposure at all, if a tool still maps those CVEs onto it, the tool's metadata is stale.
- The shaded Gson is internal and repackaged, so Gson findings do not apply to it either, but shading also means your dependency tree will not show it. Accurate SCA tooling matters here; a scanner such as Safeguard resolves what a jar actually contains rather than pattern-matching artifact names, which is the difference between one real finding and five phantom ones.
The verification pattern that prevents the classic JWT bugs
The attacks that actually compromise JWT systems are protocol-level: accepting alg: none, letting an attacker downgrade RS256 to HS256 so the public key becomes an HMAC secret, or verifying the signature but never checking exp, iss, or aud. Nimbus gives you a processor pipeline that closes all of these when configured explicitly:
ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
// Remote JWKS with caching and rate limiting built in
JWKSource<SecurityContext> keySource =
JWKSourceBuilder.create(new URL("https://issuer.example.com/.well-known/jwks.json"))
.build();
// Pin the ONE algorithm you issue tokens with
JWSKeySelector<SecurityContext> keySelector =
new JWSVerificationKeySelector<>(JWSAlgorithm.RS256, keySource);
jwtProcessor.setJWSKeySelector(keySelector);
// Enforce claims, not just signatures
jwtProcessor.setJWTClaimsSetVerifier(new DefaultJWTClaimsVerifier<>(
new JWTClaimsSet.Builder().issuer("https://issuer.example.com").build(),
Set.of("sub", "exp", "aud")));
JWTClaimsSet claims = jwtProcessor.process(tokenString, null);
Read what this construction refuses to do. The key selector accepts RS256 and nothing else, so alg: none and HS256-downgrade tokens fail before any cryptography runs, the algorithm comes from your configuration, never from the token header. The claims verifier makes expiry, issuer, and required-claim checks part of processing rather than an afterthought someone forgets in one code path. And the JWKS source handles key rotation, caching, and outage behavior instead of your hand-rolled HTTP client.
The anti-pattern to grep for in code review:
// DANGEROUS: parses without verifying anything
SignedJWT jwt = SignedJWT.parse(token);
String user = jwt.getJWTClaimsSet().getSubject();
parse() decodes; it does not verify. Any code that reads claims between parse() and a successful verify()/process() call is trusting attacker input. This single mistake accounts for a depressing share of JWT authentication bypasses across all languages, and it is invisible to dependency scanners because the library is doing exactly what it was asked. Exercising your token endpoints with tampered tokens, stripped signatures, and swapped algorithms is DAST territory, and worth automating for anything that mints or accepts JWTs.
Hardening details that separate fine from good
- Enforce expected token type. Set a
JOSEObjectTypeVerifier(e.g. expectingat+jwtfor access tokens) so a refresh token or ID token cannot be replayed where an access token belongs. - Cap what you accept. Reject tokens over a sane byte length before parsing, and if you use JWE with PBES2 anywhere, confirm you are past 9.37.2 so the iteration-count cap protects you.
- Key rotation without downtime. Publish new keys in the JWKS before signing with them; Nimbus's
kid-based selection then rotates cleanly. Keep old verification keys published until the last token signed with them expires. - Do not build sessions out of long-lived JWTs. Short expiry plus refresh flow beats a revocation-list bolted on later. The library will happily verify a ten-year token; the design review should not.
- Stay current. The 10.x line continues the project's habit of small, frequent releases. A JOSE library is a poor place to run three years behind, whatever your framework pins.
Where it sits among Java JWT libraries
The short comparison: jjwt offers a friendlier fluent API for straightforward JWS use; java-jwt (Auth0) is similarly focused. Nimbus is the only one of the three with full JWE, JWK, and OpenID Connect coverage, which is why frameworks embed it and why identity-heavy systems end up on it whether they chose it or not. If your needs are "sign and verify RS256 access tokens", any of the three is fine, configured correctly; if you need encrypted tokens, JWKS handling, or OIDC plumbing, you will land on Nimbus eventually, so learning its processor API pays off.
FAQ
What are the Maven coordinates for nimbus-jose-jwt?
Group ID com.nimbusds, artifact ID nimbus-jose-jwt. It is developed by Connect2id under the Apache 2.0 license, and the 10.x line has been current since January 2025.
Does nimbus-jose-jwt have known vulnerabilities?
The notable direct CVE is CVE-2023-52428, a denial of service via oversized PBES2 p2c iteration counts in JWE headers, fixed in 9.37.2. Many older scanner findings actually targeted its former json-smart dependency, which was removed in version 9.24 in favor of a shaded Gson.
How do I prevent algorithm confusion attacks with nimbus-jose-jwt?
Configure a JWSVerificationKeySelector with the exact algorithm you issue (for example RS256 only) inside a DefaultJWTProcessor. The accepted algorithm then comes from your code, not the token's alg header, which defeats both none and RS256-to-HS256 downgrade attempts.
Is parsing a JWT the same as verifying it?
No. SignedJWT.parse() only decodes the token; claims are untrusted until process() or an explicit verify() with the right key succeeds. Reading claims from a parsed-but-unverified token is the most common JWT implementation bug.