The official MicroProfile JWT 2.2 TCK verifies that an implementation correctly accepts, rejects and exposes a wide range of forged tokens against an Arquillian reference application — inline keys, JWKS, JWE, standard claims, MP claims, Authorization and cookie transport, key rotation, falsified signatures, exp / nbf / iat at the boundaries. Cervantes passes it at 208 / 208 NEW (MicroProfile JWT Auth 2.2 TCK, run on 2026-10-04). The 2.1 release was passed at 206 / 206 (May 2026, milestone M6); the 2.2 suite adds RsaAndEcSignatureAlgorithmTest (2 tests: RS256 and ES256 tokens verified against one mixed RSA+EC JWKS) and drops the EJB, JACC and Servlet container tests.

Status

Metric Value

Tests executed

208

Passed

208 ✅

Failed

0

Errors

0

Skipped

0

Any future regression must be logged in the repository’s TCK.md with: test name, reason, remediation plan.

Prerequisites

The org.eclipse.microprofile.jwt:microprofile-jwt-auth-tck:2.2 artefact (and its tests classifier) is now available on Maven Central — it is downloaded automatically. The runner also installs cassini-tck into the local M2 to provide CassiniTestHarness.

sdk env                              # Java 25 + Maven 3.9.16
./mvnw -ntp install -DskipTests     # installs cervantes-core, cervantes-jaxrs, cervantes-cdi-vauban, …

Runner execution

The run-official-tck-mp-jwt-2.2.sh script at the repository root drives every variant.

# Smoke — single fast test to validate the installation
./run-official-tck-mp-jwt-2.2.sh

# Full suite — equivalent to the official 208-test TCK
./run-official-tck-mp-jwt-2.2.sh all

# Targeted test via -Dtest=
./run-official-tck-mp-jwt-2.2.sh -Dtest=PublicKeyAsPEMTest

The runner uses Arquillian. The Surefire report lands in cervantes-tck/target/surefire-reports/.

Runner architecture

cervantes-tck is in-reactor behind the tck Maven profile of cervantes-parent (TCK harmonisation, vidocq-runtime-tck-* pattern): a plain mvn install never builds nor downloads it. Direct invocation: ./mvnw -P"tck,smoke" -pl cervantes-tck test (smoke) or ./mvnw -P"tck,tck-official" -pl cervantes-tck test (full suite). Template originally mirrored from heisenberg-tck.

The runner was historically detached from the reactor (standalone modelVersion 4.0.0 POM with no <parent>) because ShrinkWrap Maven Resolver 3.3, transitively pulled in by the official TCK, could not parse Model 4.1.0 POMs. That constraint no longer applies since the workspace migrated to Maven 3.9.16 / Model 4.0.0, allowing the reintegration behind the tck profile.

Stack assembled by the runner:

Layer Component

Spec

microprofile-jwt-auth-api 2.2, microprofile-jwt-auth-tck 2.2 (+ tests classifier)

MP JWT implementation

cervantes-mp-jwt-api, cervantes-api, cervantes-core, cervantes-cdi-vauban, cervantes-jaxrs

CDI container

Vauban (io.vidocq.vauban.core) in embedded mode — instantiated per Arquillian deployment

Arquillian container

CervantesJwtDeployableContainer — for each ShrinkWrap archive: extracts the bean classes, copies the archive’s META-INF/microprofile-config.properties to system properties, starts Cassini + Chappe on a random port, rewrites mp.jwt.verify.publickey.location=http://localhost:8080/… to the actual ephemeral port

HTTP transport

Chappe (io.vidocq.chappe.http)

Test framework

TestNG (arquillian-testng-container), executed by Maven Surefire through the surefire-testng provider

Documented exclusions

One exclusion, not applicable to the Core + JWT profile targeted by Cervantes:

Exclusion Justification

<excludedGroups>ee-security-optional</excludedGroups>

TestNG group for group-to-role mapping via the Jakarta EE Security API — marked optional by the spec, not required for MP JWT 2.2 conformance, and not implemented.

The MP JWT 2.2 tests jar no longer ships the EJB, JACC and Servlet container tests. The 2.1 runner excluded those three packages by path, and the exclusions were removed from cervantes-tck/pom.xml with the bump to 2.2.

The exclusion is declared in the Surefire configuration of cervantes-tck/pom.xml (<excludedGroups>). Any future exclusion must be tracked in TCK.md with its justification.

Gaps resolved during the race to 100 %

The TCK surfaced several gaps between the spec and the initial implementation; every one of them was fixed in the engine, not masked by exclusions:

Component Resolved gap

KeyResolvers

Lazy HTTP load + PEM-vs-JWKS auto-detection on the same URL + URL re-read after the server starts (the port is only known after bind).

JwksSource

Added Accept: application/json header (§9.2.2) + classpath: resolution honoured.

JwkParser

Parsing of JWK private key (d) and certificate (x5c).

Jwe / JweDecryptor

Explicit required-algorithm check + cty=JWT check for nested JWE.

ClaimResolver

raw_token exposed as JsonString, single aud converted to a single-element array.

CervantesClaimExtension

Unwrap Provider<T> and Instance<T> in injection-point type detection.

JwtAuthenticationFilter

Cookie-based token extraction (§9.2.3), error-case distinction, anonymous behaviour on missing token.

DefaultJsonWebToken

Anonymous principal → null for any requested claim.

Prerequisites delivered upstream

Two external fixes were necessary to reach 206/206 on the 2.1 TCK (still part of the 2.2 score):

  • cassini — @Context SecurityContext patch injected into a resource to reflect the JWT from JwtAuthenticationFilter. Merged on Cassini main, REST TCK 4.0 (2535 tests) preserved.

  • vauban — VAU-INJ-PRIM fix: TypeMapper was exposing a primitive injection point (boolean) as ClassType[name=boolean] instead of PrimitiveType, silently breaking @Claim boolean injection. Fix + regression test PrimitiveQualifiedInjectionTest. Merged on Vauban main.

Quality contract

Any structural change to cervantes-core, cervantes-cdi-vauban or cervantes-jaxrs must preserve the 208 / 208 score before merge. A TCK regression is a CI blocker: the PR does not pass.

Further reading

  • Internals — understand what the TCK validates.

  • Reference — annotations and keys exercised by the tests.

  • BUG.md — tracked reproducible bugs.

  • TCK.md — score history.