Cervantes is the MicroProfile JWT 2.1 implementation of the Vidocq ecosystem. It validates signed (and optionally encrypted) JWT bearer tokens using java.security exclusively, parses JSON through Champollion, reads its configuration through Ravel, exposes the JsonWebToken principal in CDI through Vauban, and enforces @RolesAllowed on JAX-RS endpoints through Cassini. Goal: 100 % conformance to the MicroProfile JWT 2.1 TCK (achieved, 206 / 206 at M6), zero third-party crypto or JWT library, strict JPMS, jlink-ready.

Origin of the name

Miguel de Cervantes (1547-1616), Spanish writer. Author of The Ingenious Hidalgo Don Quixote of La Mancha (1605 / 1615), the first great work of modern literature. See the Wikipedia article.

Don Quixote is not knighted: he proclaims himself a knight-errant and carries with him the proofs of his rank — arms painted on his shield, copied letters patent, a heraldic narrative recited at every encounter. The picaresque story runs on that very mechanism: every interlocutor receives the claim, examines it, holds it as true or as folly. Cervantes invented the fiction of the bearer of a signed identity — a character travelling with his own credentials and demanding that the world verify them.

A JWT is exactly that: a bearer token that presents a signed identity on every HTTP request, and that each service verifies locally, without contacting a central authority. Cervantes the runtime, unlike the windmills of La Mancha, does not take the bearer’s claims at face value: it actually verifies the signature, issuer, audience and expiration, and grants a role only after the check.

Cervantes, the writer Cervantes, the JWT runtime

Don Quixote carries his letters patent

Each request carries its Authorization: Bearer ey…

Local verification, no king to consult

Local validation via public key (offline, no callback to the issuer)

Heraldic narrative: name, arms, lineage

Claims: iss, sub, aud, groups, roles

Donor’s seal

Digital signature (RS256, ES256, …)

Dated and expirable letters

exp, nbf, iat with clock-skew tolerance

Lineage proved by a chain of witnesses

Chain of trust via the JWK Set published at iss

Knighthood disputed by the curate and the barber

401 if the signature lies, 403 if the role is missing

At a glance

Implemented spec

https://microprofile.io/specifications/microprofile-jwt-auth/

Repo

https://codeberg.org/Vidocq/cervantes

Java

25 (LTS)

JPMS modules

io.vidocq.cervantes.api, io.vidocq.cervantes.core, io.vidocq.cervantes.cdi.vauban, io.vidocq.cervantes.jaxrs, org.eclipse.microprofile.jwt (repackaging)

Public packages

io.vidocq.cervantes.api. (stable SPI), io.vidocq.cervantes.cdi. (CDI integration), io.vidocq.cervantes.jaxrs.* (JAX-RS integration)

Runtime dependencies

microprofile-jwt-auth-api 2.1, jakarta.json-api (via Champollion), jakarta.enterprise.cdi-api 4.1, jakarta.ws.rs-api 4.0, jakarta.annotation-api 3.0, io.vidocq.ravel:ravel-mp-config-api

Crypto

java.security (Signature, KeyFactory), javax.crypto (JWE) — no third-party library

jlink-ready

✅ — every sub-module ships its own module-info.java, no Automatic-Module-Name fallback

MP JWT 2.1 TCK

206 / 206 (100 %) — see detailed status

Three identifying traits

  1. Zero third-party crypto or JWT library. No Nimbus, no jose4j, no Bouncy Castle, no Auth0 java-jwt, no SmallRye JWT. All signature verification (RS256/384/512, ES256/384/512, JOSE R‖S ⇄ DER transcoding for ECDSA) runs through java.security.Signature. JWE decryption (RSA-OAEP, A256GCM) runs through javax.crypto. JSON parsing runs through Champollion (JSON-P 2.1).

  2. Native @RolesAllowed without patching Cassini. The JAX-RS integration sets a SecurityContext backed by the validated JsonWebToken and enforces @RolesAllowed / @PermitAll / @DenyAll through a DynamicFeature. Method precedence over class. 401 on invalid signature, 403 on missing role — entirely through the standard JAX-RS API, no implementation patch.

  3. Strict JPMS, virtual-thread-friendly, AOT-friendly. Every artefact has its own module-info.java. io.vidocq.cervantes.internal.* is never exported. No setAccessible(true) in production, no dynamic Proxy. JWKS fetching uses java.net.http.HttpClient, with no synchronized around I/O. Compatible with GraalVM native-image and Leyden CDS.

Position in the ecosystem

Diagram

Cervantes consumes the ecosystem from the bottom up: Chappe serves the requests, Cassini hosts the auth filter and the @RolesAllowed DynamicFeature, Vauban triggers the BCE that synthesises the @Claim beans, Ravel resolves the mp.jwt.* keys, Champollion parses the JSON payload of the token. Cervantes adds the signed-identity layer — without rewriting anything else.

  • Getting started — first protected endpoint in under five minutes.

  • Concepts — JWT, standard and MP claims, signature, JWK Set, rotation.

  • Usage patterns@RolesAllowed, @Claim, JWKS, JWE, cookies, refresh.

  • Reference — artefacts, annotations, mp.jwt.* keys, algorithms.

  • Internals — pure engine, auth filter, JWKS cache, threading.

  • TCK — execution, status 206 / 206, exclusions.

  • Migration — from SmallRye JWT.