Developing RESTful APIs using JAX-RS is not that difficult. However, authentication
and authorization of these APIs require additional consideration. In this article,
we will see how an API, that is developed using JAX-RS, can be secured using
OpenID Connect. The complete working source code can be downloaded from
GitHub. This code is built using Nimbus library to parse and verify a JWT.
While the solution presented here is specific to JAX-RS based RESTful service but the
same concept (and most code) can be applied to protect a RESTful service in any Web container
that supports HTTP filter.
1. Identity Provider (IDP)
Identity Provider is the central part of the security architecture. There are various
open source OIDC solutions available, e.g. Keycloak, in addition to various popuplar
social providers like Google, Facebook and Twitter.
2. Relying Party (Client)
Relying party (RP) is the web, mobile application that requests the identity token from
an OpenID Connect (OIDC) provider. An RP requires one time registration with the IDP
and needs to provide Web Origin and Redirect URLs. At least this is the case with Keycloak.
How an RP gets the identity token from an IDP is not the scope of this article. However,
application to obtain an identity token via Web Browser which initiates HTTP redirects
for authentication and then get redirected by IDP back to the Web application with the
3. Identity Token (JWT)
The identity token obtained from an OIDC server is a standard JSON Web Token (JWT)
packaged in a simple JSON object.
The IDP can be configured to return more claims like email, first name, last name,
organization etc. in the JWT.
4. JAX-RS Filter for JWT
A basic solution to secure a set of REST endpoints running in a Java Web container is to
install a security filter. A typical security filter intercepts all the incoming requests
and examins authentication and applies authorization logic based on the security context.
The JAX-RS API allows us to subclass javax.ws.rs.container.ContainerRequestFilter to
intercept incoming request to a REST endpoint. It also allows us to apply a name-binding
annotation to specific REST endpoint so that the filter does not intercept each and
every service request.
We can implement a custom javax.ws.rs.core.SecurityContext to stuff in the
claims received from JWT so that rest of the JEE stack can work with the standard
annotation @RolesAllowed and API JWTPrincipal p = (JWTPrincipal) securityContext.getUserPrincipal();
A custom implementation of java.security.Principal can be used to map claims
from JWT and then used in the custom SecurityContext.
Protect a sample REST API
This example shows how to use standard JEE annotation and API to protect a REST API.