MicroProfile JWT Auth bridge for GuicedEE β maps the MP JWT JsonWebToken interface and @Claim injection to the Vert.x JWT Auth provider used by GuicedEE.
Built on MicroProfile JWT Β· Vert.x Auth JWT Β· Google Guice Β· JPMS module com.guicedee.microprofile.jwt Β· Java 25+
<dependency>
<groupId>com.guicedee.microprofile</groupId>
<artifactId>jwt</artifactId>
</dependency>Gradle (Kotlin DSL)
implementation("com.guicedee.microprofile:jwt:2.0.2-SNAPSHOT")JsonWebTokeninjection β backed by Vert.xUserprincipal, resolved per-request from the GuicedEECallScope@Claiminjection without@Injectβ@Claim("sub")fields are auto-injected viaInjectionPointProviderandNamedAnnotationProviderSPIs, mirroring the@ConfigPropertypattern- Type-safe claim bindings β
String,Set<String>,Long,Integer,Boolean, andOptional<T>wrappers supported per claim - Seamless bridge β works with
@JwtAuthOptionsfrom thecom.guicedee.vertxauth module - CallScope-aware context β JWT context stored in GuicedEE
CallScopePropertiesfor correct behavior across Mutiny/reactive thread switches; falls back toThreadLocaloutside call scopes - Optional auth dependency β auth provider classes are accessed via reflection; the module works without the JWT auth provider on the module path
- JPMS-ready β named module with proper exports, provides, and opens directives
Step 1 β Configure JWT auth on your package-info.java (see Vert.x Auth docs):
@JwtAuthOptions(
keystorePath = "keystore.jceks",
keystoreType = "jceks",
keystorePassword = "${JWT_KEYSTORE_PASSWORD}",
algorithm = "RS256",
authorizationType = JwtAuthorizationType.MICROPROFILE
)
package com.example.auth;Step 2 β Inject MP JWT types (no @Inject needed on @Claim fields):
import jakarta.inject.Inject;
import org.eclipse.microprofile.jwt.Claim;
import org.eclipse.microprofile.jwt.JsonWebToken;
public class UserService {
@Inject
private JsonWebToken jwt;
@Claim("sub")
private String subject;
@Claim("groups")
private Set<String> groups;
@Claim("exp")
private long expiration;
public String currentUser() {
return subject;
}
}Step 3 β Register via JPMS:
module com.example {
requires com.guicedee.microprofile.jwt;
opens com.example to com.google.guice;
}flowchart TD
A["IGuiceContext.instance()"] --> B["IGuicePreStartup hooks"]
B --> B1["MicroProfileJwtPreStartup<br/>validates JWTAuth provider via reflection"]
A --> C["IGuiceModule hooks"]
C --> C1["MicroProfileJwtModule"]
C1 --> C1a["bind JsonWebToken via MicroProfileJwtContext"]
C1 --> C1b["scan @Claim fields via ClassGraph"]
C1 --> C1c["bind per type: String, Set, Long, Integer, Boolean, Object"]
A --> D["SPI Registrations"]
D --> D1["ClaimInjectionPointProvider<br/>registers @Claim as injection point"]
D --> D2["ClaimNamedAnnotationProvider<br/>converts @Claim to @Named"]
D --> D3["ClaimBindingAnnotationProvider<br/>registers @Qualifier as binding annotation"]
flowchart LR
subgraph mp["MicroProfile JWT API"]
jwt["JsonWebToken"]
claim["@Claim('sub')"]
claims["Claims enum"]
end
subgraph vx["Vert.x Auth"]
principal["User.principal()"]
attrs["User.attributes()"]
jwtauth["JWTAuth"]
end
principal -->|"bridges via"| bridge["VertxJsonWebToken"]
attrs --> bridge
bridge --> jwt
jwtauth -->|"configured by"| provider["JwtAuthenticationProvider<br/>from com.guicedee.vertx"]
MicroProfileJwtModule scans all classes with @Claim-annotated fields and creates Guice bindings for each:
| Field type | Binding | Example claim |
|---|---|---|
String |
Direct value from JWT claim | sub, iss, jti |
Set<String> |
JSON array β Set<String> |
groups, aud |
long / Long |
Numeric claim | exp, iat |
int / Integer |
Numeric claim | custom claims |
boolean / Boolean |
Boolean claim | custom claims |
Optional<String> |
Wrapped optional | any claim |
Object |
Raw claim value (fallback) | any claim |
The JWT context is managed per-request via MicroProfileJwtContext:
// In a route handler or filter:
MicroProfileJwtPreStartup.setCurrentUser(routingContext.user());
try {
// handle request β @Claim fields resolve from this context
} finally {
MicroProfileJwtContext.clear();
}| Environment | Storage | Thread-safe across switches |
|---|---|---|
Inside CallScope (HTTP handlers) |
CallScopeProperties.properties map |
β Yes |
Outside CallScope (tests, CLI) |
ThreadLocal fallback |
β Yes (same thread) |
All SPIs are discovered via ServiceLoader. Register implementations with JPMS provides...with or META-INF/services.
| SPI | Implementation | Purpose |
|---|---|---|
IGuicePreStartup |
MicroProfileJwtPreStartup |
Validates JWT auth provider availability at startup |
IGuiceModule |
MicroProfileJwtModule |
Scans @Claim fields, binds JsonWebToken and claims to Guice |
InjectionPointProvider |
ClaimInjectionPointProvider |
Registers @Claim as a Guice injection point (no @Inject needed) |
NamedAnnotationProvider |
ClaimNamedAnnotationProvider |
Converts @Claim("x") β @Named("x") for binding keys |
BindingAnnotationProvider |
ClaimBindingAnnotationProvider |
Registers @jakarta.inject.Qualifier as a binding annotation |
flowchart LR
jwt["com.guicedee.microprofile.jwt"] --> vertx["com.guicedee.vertx<br/>Vert.x lifecycle, CallScope, auth SPIs"]
jwt --> injection["com.guicedee.guicedinjection<br/>GuicedEE runtime β classpath scanning"]
jwt --> client["com.guicedee.client<br/>SPI contracts β IGuiceContext, CallScopeProperties"]
jwt --> authjwt["io.vertx.auth.jwt<br/>Vert.x JWT Auth β optional, via reflection"]
jwt --> mpjwt["microprofile.jwt.auth.api<br/>MicroProfile JWT API β JsonWebToken, @Claim, Claims"]
jwt --> jakjson["jakarta.json<br/>Jakarta JSON API β required by MP JWT Claims enum"]
jwt --> classgraph["io.github.classgraph<br/>annotation scanning"]
jwt --> guice["com.google.guice<br/>Guice DI"]
| Class | Package | Role |
|---|---|---|
VertxJsonWebToken |
jwt |
JsonWebToken implementation backed by Vert.x User principal |
MicroProfileJwtContext |
jwt |
CallScope-aware holder for the current request's JsonWebToken |
ClaimLiteral |
jwt |
Runtime @Claim annotation implementation for Guice binding keys |
ClaimValueProvider |
jwt |
Guice Provider<T> that resolves claims from the current JWT |
MicroProfileJwtModule |
implementations |
IGuiceModule β scans @Claim fields and creates per-type bindings |
MicroProfileJwtPreStartup |
implementations |
IGuicePreStartup β validates JWT auth provider via reflection |
ClaimInjectionPointProvider |
implementations |
Registers @Claim as a Guice injection point |
ClaimNamedAnnotationProvider |
implementations |
Converts @Claim("x") β @Named("x") |
ClaimBindingAnnotationProvider |
implementations |
Registers @Qualifier as a binding annotation |
Module name: com.guicedee.microprofile.jwt
The module:
- exports
com.guicedee.microprofile.jwt,com.guicedee.microprofile.jwt.implementations - requires transitive
com.guicedee.vertx,com.guicedee.guicedinjection,io.vertx.auth.jwt,microprofile.jwt.auth.api,jakarta.json - provides
IGuiceModulewithMicroProfileJwtModule - provides
IGuicePreStartupwithMicroProfileJwtPreStartup - provides
InjectionPointProviderwithClaimInjectionPointProvider - provides
NamedAnnotationProviderwithClaimNamedAnnotationProvider - provides
BindingAnnotationProviderwithClaimBindingAnnotationProvider - opens
com.guicedee.microprofile.jwtandcom.guicedee.microprofile.jwt.implementationstocom.google.guice
import com.guicedee.microprofile.jwt.MicroProfileJwtContext;
import com.guicedee.microprofile.jwt.VertxJsonWebToken;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.User;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class JwtBridgeTest {
@Test
void claimsAreResolvedFromVertxUser() {
User user = User.create(new JsonObject()
.put("sub", "user-123")
.put("iss", "https://auth.example.com")
.put("groups", new io.vertx.core.json.JsonArray().add("admin")));
VertxJsonWebToken jwt = new VertxJsonWebToken(user);
assertEquals("user-123", jwt.getSubject());
assertEquals("https://auth.example.com", jwt.getIssuer());
assertTrue(jwt.getGroups().contains("admin"));
}
@Test
void contextSetAndClear() {
User user = User.create(new JsonObject().put("sub", "test"));
MicroProfileJwtContext.setCurrent(new VertxJsonWebToken(user));
assertNotNull(MicroProfileJwtContext.getCurrent());
assertEquals("test", MicroProfileJwtContext.getCurrent().getSubject());
MicroProfileJwtContext.clear();
assertNull(MicroProfileJwtContext.getCurrent());
}
}Issues and pull requests are welcome β please add tests for new claim types, bridge behaviors, or context strategies.