All Jakarta EE templates

MicroProfile JWT Authentication

JWT bearer auth with @LoginConfig, claim injection, and role-based access.

DevZone Tools940 copiesUpdated Feb 25, 2026MicroProfileJakarta EEJava
# CLAUDE.md — MicroProfile JWT Authentication

## Setup

- Add the MP-JWT spec to your runtime (Quarkus: `quarkus-smallrye-jwt`; Open Liberty: bundled).
- On the application class:
  ```java
  @LoginConfig(authMethod = "MP-JWT", realmName = "myapp")
  @ApplicationPath("/")
  public class App extends Application {}
  ```
- Configure the public key for verification:
  ```
  mp.jwt.verify.publickey.location=https://auth.example.com/.well-known/jwks.json
  mp.jwt.verify.issuer=https://auth.example.com
  mp.jwt.verify.audiences=myapp
  ```

## Securing endpoints

- Use Jakarta Security annotations:
  ```java
  @GET @RolesAllowed("user")
  public List<Order> myOrders() { ... }

  @POST @RolesAllowed("admin")
  public void deleteUser(@PathParam("id") String id) { ... }
  ```
- `@PermitAll` on public endpoints — be explicit so future developers don't break it accidentally.
- `@DenyAll` on placeholder methods you haven't authorized yet.

## Reading claims

- Inject the full token: `@Inject JsonWebToken jwt`.
- Inject specific claims:
  ```java
  @Inject @Claim("email") String email;
  @Inject @Claim("groups") Set<String> groups;
  @Inject @Claim("sub") String userId;
  ```
- Standard claims: `iss`, `sub`, `aud`, `exp`, `iat`, `groups`. Custom claims work the same way.

## Roles

- The `groups` claim is mapped to roles by default. Configure the claim name if your provider uses a different name:
  ```
  mp.jwt.claim.roles.path=resource_access.myapp.roles
  ```
- Don't trust the token blindly. Validate signature, issuer, audience, expiry. The library does this; just configure correctly.

## Token issuance

- MP-JWT specifies the **verification** side — issuance is up to your auth provider (Keycloak, Auth0, Okta, AWS Cognito).
- For dev, use **smallrye-jwt-build** to issue tokens locally. Never use it in production.
- Pin the JWKS URL to a fixed location — don't accept arbitrary issuers.

## Token transport

- Standard `Authorization: Bearer <token>` header.
- For browser apps, store in HttpOnly Secure cookies. The MP-JWT extension can read from cookies if configured:
  ```
  mp.jwt.token.header=Cookie
  mp.jwt.token.cookie=jwt
  ```
- Never store JWTs in `localStorage`.

## Refresh

- MP-JWT doesn't define refresh. Implement at the auth provider — clients send refresh tokens to the auth server, get fresh access tokens.
- Keep access tokens short-lived (15 min). Refresh tokens longer-lived but revocable server-side.

## Don't

- Don't put sensitive data (PII, plan info) in JWT claims. The token is base64url-decodable by anyone.
- Don't accept tokens without verifying the signature.
- Don't use `@RolesAllowed` and ignore object-level authorization. JWT proves identity; ownership checks happen in the service.
- Don't issue long-lived (months) access tokens to "avoid" refresh complexity.

Other Jakarta EE templates