This episode covers OAuth2 and OIDC in Quarkus: integration with an external identity provider, implementing JWT authentication and authorization, service-to-service auth with token introspection, and token storage and refresh best practices.

Basic auth is enough for small applications, but modern applications need identity federation: users log in at one place (Google, GitHub, Keycloak) and then access many services. This is where OAuth2 and OpenID Connect (OIDC) come in.
Episode 13 covers Quarkus integration with an OIDC external identity provider, implementing JWT authentication and authorization, service-to-service auth with token introspection, as well as token storage, refresh, and scope best practices.
./mvnw quarkus:add-extension -Dextensions=oidcThe command ./mvnw quarkus:add-extension -Dextensions=oidc adds OIDC support to your project.
Connect Quarkus to an identity provider (for example Keycloak) via configuration:
quarkus.oidc.auth-server-url=https://auth.example.com/realms/demo
quarkus.oidc.client-id=quarkus-app
quarkus.oidc.credentials.secret=client-secret
quarkus.oidc.tls.verification=certificate-validationauth-server-url: the OIDC provider realm endpoint.client-id and credentials.secret: application credentials for the authorization code flow.tls.verification: make sure the server certificate is validated.Once configured, endpoints can be protected with configuration only:
quarkus.http.auth.permission.authenticated.paths=/api/*
quarkus.http.auth.permission.authenticated.policy=authenticatedThis rule rejects every request to /api/* that doesn't carry a valid token from the provider.
A JWT (JSON Web Token) is a token in the header.payload.signature format. The payload contains claims like sub, exp, and roles. The signature guarantees the token hasn't been modified. Quarkus validates the signature using a key from the OIDC provider — without calling the server on every request.
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.jwt.JsonWebToken;
@Path("/api/user")
public class UserResource {
@Inject
JsonWebToken jwt;
@GET
@RolesAllowed("user")
public String info() {
return "Halo " + jwt.getSubject();
}
}jwt.getSubject() returns the sub claim — the user's identity. The @RolesAllowed("user") annotation verifies the roles claim in the token.
Make sure the identity provider sends the roles claim. In Keycloak, mapping roles into this claim is configured in the client scope. If the claim name is different, map it via configuration:
quarkus.oidc.roles.claim=rolesNot every service uses JWTs. Opaque tokens need to be verified through an introspection endpoint:
quarkus.oidc.token.audience=quarkus-app
quarkus.oidc.token-issuer=https://auth.example.com/realms/demoWhen a client sends an opaque token to Authorization: Bearer, Quarkus calls the provider's introspection endpoint to validate it and fetch the claims. This is commonly used for communication between internal services.
Service-to-service communication without user intervention uses the client credentials flow:
import io.quarkus.oidc.client.OidcClient;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@ApplicationScoped
public class InternalApiClient {
@Inject
OidcClient oidcClient;
public String panggilServiceInternal() {
String token = oidcClient.getTokens().await().indefinitely()
.getAccessToken();
return kirimRequest(token);
}
}oidcClient.getTokens() obtains an access token with client credentials. This token is used to call other internal services — a common pattern in microservices architectures.
In the browser, keep the access token in memory, not in localStorage (which is vulnerable to XSS). For native applications, store it securely in the keychain. On the server, never log tokens — a JWT contains sensitive identity information.
Request the smallest possible scope:
quarkus.oidc.scopes=openid,profilequarkus.oidc.scopes determines which claims are requested from the provider. The less data you request, the smaller your attack surface.
Access tokens are short-lived (for example 5 minutes); a refresh token obtains a new access token without re-authentication. Quarkus manages this automatically for web-based applications. The golden rule: the refresh token is only sent to the server, never to client-side JavaScript. For logout, call the provider's logout endpoint:
quarkus.oidc.logout.path=/api/logout
quarkus.oidc.logout.post-logout-path=/Episode 13 brings you to modern security: understanding Quarkus OIDC integration with an external identity provider, implementing JWT authentication and authorization, service-to-service auth with token introspection and the client credentials flow, as well as token storage, refresh, and scope best practices.
Key takeaways:
@RolesAllowed reads the roles claim from the token.In episode 14 we'll cover secure microservices and the API gateway — secure microservice patterns with Quarkus, integration with Istio, Envoy, or an API gateway, securing inter-service communication, as well as rate limiting and API policies.