This page consolidates the public surface of Cervantes: artefacts to declare, exported Java modules, recognised security annotations, every mp.jwt.* key read by JwtAuthConfigProducer, and every JOSE algorithm supported by the signature and encryption engine.

Maven artefacts

groupId artifactId Role

io.vidocq.cervantes

cervantes-mp-jwt-api

MP JWT 2.1 spec repackaged into a named Java module (org.eclipse.microprofile.jwt). No dependency.

io.vidocq.cervantes

cervantes-api

Stable public SPI — JwtValidator, JwtConfig, KeyResolver, SignatureAlgorithm, JwtValidationException.

io.vidocq.cervantes

cervantes-core

Pure validation engine — JWT parsing, signature verification (RS/ES), claim validation, key loading (PEM, JWKS), JWE decryption. No CDI, no JAX-RS.

io.vidocq.cervantes

cervantes-cdi-vauban

@RequestScoped JsonWebToken producer, CervantesClaimExtension BCE for @Claim, JwtAuthConfigProducer producing the JwtValidator.

io.vidocq.cervantes

cervantes-jaxrs

JwtAuthenticationFilter (@PreMatching, AUTHENTICATION), RolesAllowedDynamicFeature + RolesAllowedRequestFilter, JwtSecurityContext.

io.vidocq.cervantes

cervantes-bench

JMH benchmarks (vs SmallRye JWT, opt-in profile -Pcompare-smallrye). Not for production.

io.vidocq.cervantes

cervantes-examples

Examples (ProtectedResource with @RolesAllowed + @Claim).

io.vidocq.cervantes

cervantes-tck

Official MP JWT 2.1 TCK runner — in-reactor, but only under the tck Maven profile (a plain mvn install never builds it). Do not declare as an application dependency.

All versions at 0.3.0. Shared parent io.vidocq:vidocq-parent:0.3.0-SNAPSHOT.

Java modules

Module Contents

org.eclipse.microprofile.jwt

Spec repackaging — annotations @Claim, @LoginConfig, interfaces JsonWebToken, ClaimValue. No requires.

io.vidocq.cervantes.api

io.vidocq.cervantes.api.* — stable SPI.

io.vidocq.cervantes.core

No unqualified export — the only export is qualified: exports io.vidocq.cervantes.internal to io.vidocq.cervantes.cdi.vauban. The stable SPI reaches consumers through requires transitive io.vidocq.cervantes.api.

io.vidocq.cervantes.cdi.vauban

exports io.vidocq.cervantes.cdi (JsonWebTokenContext, CervantesClaimExtension) and exports io.vidocq.cervantes.cdi.internal (producers and generated _VaubanComponents). provides BuildCompatibleExtension and VaubanComponentProvider.

io.vidocq.cervantes.jaxrs

io.vidocq.cervantes.jaxrs.* — filters, SecurityContext, DynamicFeature.

io.vidocq.cervantes.internal (the core engine) is exported only to io.vidocq.cervantes.cdi.vauban — application modules cannot read it, and any application class depending on it signals a regression to be fixed. The io.vidocq.cervantes.cdi.internal package, although exported for the Vauban wiring, is likewise not a supported API.

Recognised security annotations

Annotation Effect Status

@jakarta.annotation.security.RolesAllowed({"r1","r2"})

Caller must own at least one of the listed roles (a role is a string from the groups claim). Precedence: method > class.

✅

@jakarta.annotation.security.PermitAll

Endpoint open to everyone, anonymous included. Disables inherited @RolesAllowed.

✅

@jakarta.annotation.security.DenyAll

Endpoint always rejected with 403, whatever the token.

✅

@org.eclipse.microprofile.jwt.Claim(value="…")

CDI injection qualifier. The typed value is extracted from the current JsonWebToken. See the type table below.

✅

@org.eclipse.microprofile.jwt.Claim(standard=Claims.email)

Variant naming the claim by its MP standard enum (instead of a free string).

✅

@org.eclipse.microprofile.auth.LoginConfig(authMethod="MP-JWT")

Application-level marker (spec compatibility). Cervantes accepts it without specific action — the auth filter is registered anyway.

✅

Supported types for @Claim

Java type Semantics

String

Coercion: JsonString.getString(), or toString() for other scalars.

Long, long

JsonNumber.longValue(). Primitive field supported since Vauban VAU-INJ-PRIM.

Integer, int

JsonNumber.intValueExact().

Double, double

JsonNumber.doubleValue().

Boolean, boolean

JsonValue.TRUE / JsonValue.FALSE.

Set<String>

For array-typed claims (groups, aud). Deduplicated, insertion order preserved (LinkedHashSet).

jakarta.json.JsonValue

Raw value parsed by Champollion.

JsonString, JsonNumber, JsonObject, JsonArray

JSON-P sub-types.

Optional<T>

Eager. Resolved once at injection. Optional.empty() if the claim is missing.

org.eclipse.microprofile.jwt.ClaimValue<T>

Lazy MP. getValue() re-reads the current request’s token.

jakarta.inject.Provider<T>

Lazy CDI. get() re-reads the current token. (jakarta.enterprise.inject.Instance<T> is not a supported claim container — use Provider<T> instead.)

java.util.function.Supplier<T>

Lazy JDK. get() re-reads the current token.

String qualified @Claim("raw_token")

The original raw token — useful for propagation.

mp.jwt.* MicroProfile Config keys

All keys are read through ConfigProvider.getConfig() — provided by Ravel in the Vidocq ecosystem.

Signature verification

Key Effect

mp.jwt.verify.publickey

Inline public key. Accepts X.509 PEM (-----BEGIN PUBLIC KEY-----) or raw base64.

mp.jwt.verify.publickey.location

Public-key location: classpath:/…, file:/…, http://…, https://…. PEM-vs-JWKS auto-detection (format regex).

mp.jwt.verify.publickey.algorithm

Expected algorithm family (RS256, ES256, …). Default RS256. Rejects tokens whose header announces a different family.

mp.jwt.verify.issuer

Expected issuer — string. Strictly compared to the token’s iss claim.

mp.jwt.verify.audiences

Accepted audiences — comma-separated list. At least one intersection with aud required (unless empty).

mp.jwt.verify.token.age

Maximum token age, in seconds since iat. Optional — and applied only when the iat claim is present.

The standard mp.jwt.verify.clock.skew key is not read by this release: the clock-skew tolerance applied to exp and nbf is fixed at 60 seconds (JwtConfig.DEFAULT_CLOCK_SKEW). Embedders building a JwtConfig directly through cervantes-api can pass any clockSkew duration.

Token transport

Key Effect

mp.jwt.token.header

Authorization (default, Bearer scheme) or Cookie (extraction from a cookie named by mp.jwt.token.cookie).

mp.jwt.token.cookie

Cookie name carrying the token when mp.jwt.token.header=Cookie. Default Bearer.

JWE decryption (optional)

Key Effect

mp.jwt.decrypt.key

Inline decryption private key. PKCS#8 PEM.

mp.jwt.decrypt.key.location

Decryption private-key location.

mp.jwt.decrypt.key.algorithm

Expected key-wrapping algorithm: RSA-OAEP (default) or RSA-OAEP-256. If set, rejects JWEs announcing anything else.

Supported signature algorithms

Family Algorithms JDK implementation

RSA

RS256, RS384, RS512

Signature.getInstance("SHA256withRSA"/…). Keys ≥ 2048 bits recommended.

ECDSA

ES256, ES384, ES512

Signature.getInstance("SHA256withECDSA"/…). P-256/P-384/P-521 curves. Internal R‖S ↔ DER transcoding (EcdsaSignatures).

Not supported

PS256, PS384, PS512 (RSA-PSS) — HS256, HS384, HS512 (HMAC)

Rejected: SignatureAlgorithm.fromJoseName resolves only the six RS/ES values, so any other alg fails validation with unsupported or missing 'alg' header.

Supported JWE algorithms

Step Algorithm Notes

Key wrapping (alg)

RSA-OAEP, RSA-OAEP-256

Single transformation Cipher.getInstance("RSA/ECB/OAEPPadding") with an explicit OAEPParameterSpec per algorithm (SHA-1 + MGF1/SHA-1, or SHA-256 + MGF1/SHA-256) — explicit parameters avoid the JDK trap of defaulting MGF1 to SHA-1. Both algorithms are covered by MP JWT 2.1 §9.2.4 (mp.jwt.decrypt.key.algorithm).

Content encryption (enc)

A256GCM

Cipher.getInstance("AES/GCM/NoPadding").

(planned)

A128CBC-HS256

Not yet supported — to be added if a future TCK milestone requires it.

HTTP behaviour

Situation Code

No token + unannotated or @PermitAll endpoint

Endpoint runs anonymously (the filter leaves the request anonymous — pass-through).

No token + @RolesAllowed endpoint

401 Unauthorized.

Token present, invalid signature / expired exp / unexpected iss / unexpected aud / unauthorised alg

401 Unauthorized.

Valid token but missing required role

403 Forbidden.

Valid token and required role present

200 OK (method runs).

@DenyAll endpoint

403 Forbidden even with a valid token.

Compatibility

  • Java 25 (LTS), Maven 3.9.16.

  • JAX-RS 4.0 (Cassini or another compliant runtime).

  • CDI 4.1 Lite (Vauban) with BCE support.

  • MicroProfile Config 3.1+ (Ravel).

  • JSON-P 2.1 (Champollion).

  • No Jakarta EE Full Profile dependency.

Bugs and benchmarks

  • BUG.md — tracked reproducible bugs (id, symptom, minimal reproduction, status).

  • BENCH.md — JMH DefaultJwtValidator.validate vs SmallRye JWT (M7).

Further reading

  • Concepts — claims, signature, JWK Set, JWE.

  • Internals — pipeline and threading.

  • TCK — status and execution.