Cervantes is at 0.4.0-SNAPSHOT. The MicroProfile JWT 2.2 spec is fully covered (TCK 208 / 208, see TCK status), but the public SPI may still evolve before 1.0.0. This page documents the mappings to prepare a staged migration.

On the application code side, migration is trivial: Cervantes strictly consumes the standard org.eclipse.microprofile.jwt. and jakarta.annotation.security. annotations. No proprietary import to replace. The gaps lie in transport/JWE configuration and in the dependency footprint.

Upgrading from Cervantes 0.3.0 NEW

Cervantes now follows MicroProfile JWT 2.2. Application code does not change, but four things behave differently from 0.3.0:

  • An unset mp.jwt.verify.publickey.algorithm accepts RS256 and ES256 — 0.3.0 defaulted to RS256. If the application relied on that default to refuse EC-signed tokens, set mp.jwt.verify.publickey.algorithm=RS256 explicitly. When the property is set, it is now enforced as a family on every token: with RS256, an RS512 token passes and an ES256 token is rejected. See supported algorithms.

  • An EC public key in PEM needs no extra setting — with the property unset, an inline or file/classpath PEM key is read as RSA or EC from the key itself; 0.3.0 read it as RSA unless the property named an EC algorithm. See signature verification keys.

  • A bad configuration now stops the container start — an unrecognised mp.jwt.verify.publickey.algorithm or mp.jwt.decrypt.key.algorithm (exact, case-sensitive names), or an unreadable verification or decryption key, fails the start with a DeploymentException naming the property and the value. 0.3.0 started anyway: it read an unrecognised verification algorithm as RSA, rejected every encrypted token for an unrecognised decryption algorithm (CERV-004), and reported an unreadable key only when the validator was first injected. Check mp.jwt.* values that used to be tolerated, such as rs256 or rsa-oaep. See when the configuration is validated.

  • JwtConfig record — it gained a seventh component, requiredAlgorithm. The six-argument constructor is kept, so code that builds a JwtConfig still compiles; source code that uses record deconstruction patterns on JwtConfig must add the new component.

From SmallRye JWT

SmallRye JWT is the reference implementation in Quarkus and in several Jakarta EE servers. Migration is direct — Cervantes targets strict conformance to the same spec.

SmallRye JWT Cervantes Note

@RolesAllowed / @PermitAll / @DenyAll

Same annotations

No application code change.

@Inject JsonWebToken

Same

Same standard MP interface.

@Inject @Claim("…") String

Same, same supported types

Cervantes adds support for Supplier<T> on top of ClaimValue<T>, Provider<T> and Optional<T>. Instance<T> is not a supported claim container — use Provider<T> instead.

mp.jwt.verify.* keys

Same keys

publickey, publickey.location, issuer, audiences, publickey.algorithm, token.age — identical (as of MP JWT 2.2, an unset publickey.algorithm accepts both RS256 and ES256 instead of defaulting to RS256). clock.skew is not read by Cervantes: the tolerance is fixed at 60 seconds.

mp.jwt.token.* keys

Same keys

header, cookie — identical. Authorization (default) and Cookie modes both supported.

mp.jwt.decrypt.key* keys

Same keys

RSA-OAEP + A256GCM supported. A128CBC-HS256 not yet — not covered by the 2.2 TCK.

Transitive dependencies: jose4j, Jackson, …

None (java.security + Champollion)

Smaller classpath footprint. Immediate jlink compatibility. GraalVM native-image friendly.

smallrye.jwt.path.sub, …path.groups, …claims.groups

No equivalent (standard claims only)

If the issuer does not place groups in the standard location, configure a filter on the issuer side or an OASModelReader on the client side. Out of scope for standard MP JWT 2.2.

smallrye.jwt.verify.aud (alias)

mp.jwt.verify.audiences (standard key)

Rename.

smallrye.jwt.token.header / …cookie

mp.jwt.token.header / mp.jwt.token.cookie

Rename (mp.jwt. prefix instead of smallrye.jwt.).

Quarkus Dev Mode hot reload

No equivalent

The cycle is ./mvnw clean install + restart.

Notable differences

  • A128CBC-HS256 unsupported — Cervantes only implements A256GCM as JWE content-encryption algorithm (the spec default, covered by the TCK). To be added if an issuer requires it.

  • No hot reflection — @Claim injection is resolved by a Vauban BCE at CDI compile time, not by a dynamic proxy. If you relied on an exotic SmallRye typing convention (for instance @Claim List<Integer> on a numeric claim stored as a string), check that the concrete type is in the supported list (see Reference).

  • No setAccessible(true) — any business class that must receive a @Claim injection must be normally visible to CDI (@Dependent, @RequestScoped, …). If SmallRye opened runtime packages, Cervantes does not — open explicitly in module-info.java.

  • mp.jwt.verify.publickey.location — PEM-vs-JWKS auto-detection is based on the format of the content, not on the file extension. A URL returning a valid JWK Set is treated as JWKS, whether it ends with .json, .jwks, or nothing.

  • JWKS — lazy refresh — Cervantes does not fetch the JWK Set at startup. The first unknown kid triggers the fetch. Upside: fast startup, no fail-fast if the issuer is temporarily down. Downside: the first request may be slightly slower.

Classpath footprint compared

Choice Runtime libs pulled in

SmallRye JWT

smallrye-jwt, jose4j, parsson (or jackson-databind), microprofile-jwt-auth-api. Indirectly: commons-logging, sometimes bouncycastle.

Cervantes

cervantes-core, cervantes-cdi-vauban, cervantes-jaxrs, cervantes-api, cervantes-mp-jwt-api, champollion-impl (JSON-P 2.1, ~120 KB), ravel-impl (MP Config). No third-party crypto/JWT library.

Porting checklist

  1. Replace the SmallRye JWT dependency with cervantes-cdi-vauban + cervantes-jaxrs (or simply the vidocq-runtime-cervantes-jwt-extension wrapper if you package through Vidocq Runtime).

  2. Audit application annotations: they must all come from org.eclipse.microprofile.jwt. and jakarta.annotation.security.. Replace any io.smallrye.jwt.* annotation.

  3. Migrate smallrye.jwt. properties to their standard mp.jwt. equivalents.

  4. Verify that the expected claims really exist on the tokens: iss, aud, exp, groups. No exotic path-based remapping.

  5. Run unit tests: ./mvnw test.

  6. Run the TCK to validate conformance: ./run-official-tck-mp-jwt-2.2.sh (see TCK).

  7. Verify in a staging environment: a valid token passes through with 200, an expired one with 401, a missing role with 403.

  8. Measure validation latency and compare (see Reference and BENCH.md). Cervantes is ~1.5× slower than SmallRye on the RS256 hot-path — a deliberate trade-off against ~3 MB of vanished transitive dependencies.

Further reading

  • Concepts — JWT model, claims, signature, JWK Set.

  • Reference — annotations and mp.jwt.* keys.

  • BUG.md — known gotchas.

  • BENCH.md — throughput comparison with SmallRye JWT 4.6.