mvn clean test
MP REST JWT with Public key from MP Config
This is an example of how to configure and use MicroProfile JWT 1.1 in TomEE.
Run the test
This project includes a sample application and an Arquillian test to showcase role based access control (RBAC) with JWT in MicroProfile. In order to run the scenario, you can execute the following command:
The application represents a book store REST resource with a few endpoints. They all expect that the client provides a valid JSON web token (JWT) representing a user having certain roles. The Arquillian test is responsible for generating the JWTs and attaching them to the HTTP requests.
Configuration in TomEE
In order to enable JWT at all, you need to annotate your REST application class with the org.eclipse.microprofile.auth.LoginConfig
annotation.
In this example, the class is ApplicationConfig
.
Another thing that needs to be done is configuring the public key to verify the signature of the JWT that is attached in the Authorization
header.
It is signed upon creation with the issuer private key.
This is done to avoid tempering with the token while it travels from the caller to the endpoint.
Usually the JWT issuing happens in a special module or microservice responsible for authenticating the users.
In this sample project this happens in the BookstoreTest
.
Each MicroProfile JWT supporting runtime should be able to check whether the signature is correct and whether the signed content is not changed along the way.
In order to do that, it needs to have access to a public key.
This public key may be in PKCS#8 PEM, JWK or JWKS format.
Since MP JWT 1.1 (which is supported by TomEE), the key may be provided as a string in the mp.jwt.verify.publickey
config property or as a file location or URL specified in the mp.jwt.verify.publickey.location
config property.
In this sample project you can see the first option.
The file src/main/resource/META-INF/microprofile-config.properties
contains the following entry:
mp.jwt.verify.publickey=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEqFyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwRTYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5eUF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYnsIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9xnQIDAQAB
Working with JWT
The BookResource
class in this sample project shows two cases where you can use the MP JWT spec: obtaining the value of a JWT claim and role based access control of REST endpoints.
Obtaining claim values
The JSON web token (JWT) attached in the Authorization
HTTP header is essentially a JSON object containing various attributes.
Those attributes are called claims.
You can obtain the value of each claim inside a CDI bean by injecting it and qualifying it with the @Claim
annotation.
For example, if you want to retrieve the preferred username claim, you can do it like that:
@Inject
@Claim(standard = Claims.preferred_username)
private String userName;
Note that you cannot inject claims this way in a REST resource class that contains unauthenticated endpoints too. TomEE will nevertheless try to extract the claim from the JWT. So if there is no JWT or if the claim is not there, the request will fail. |
Role based access control (RBAC)
One of the standard claims defined in the MP JWT specification is groups
.
It contains a list of strings, which represent the groups to which the caller belongs.
The specification does not distinguish user roles and user groups.
So the groups
claim may also contain the roles assigned to a given user.
In this regard, MP JWT has great integration with the existing Java EE security mechanisms, such as the @RolesAllowed
annotation.
So the following BookResource
method can be called by users that are either in the reader
or in the manager
role (or in both):
@GET
@Path("/{id}")
@RolesAllowed({"manager", "reader"})
public Book getBook(@PathParam("id") int id) {
return booksBean.getBook(id);
}
However, the method below will result in HTTP code 403 if called by a user that lacks the manager
role in its groups
claim:
@POST
@RolesAllowed("manager")
public void addBook(Book newBook) {
booksBean.addBook(newBook);
}
The bookstore test
The sample project contains an Arquillian test (org.superbiz.bookstore.BookstoreTest
) used for a couple of reasons:
-
Generating the JSON web token (JWT)
-
Showcasing the behavior of TomEE in different situations
-
Retrieving a claim value
-
Calling REST endpoints with appropriate roles
-
Calling a REST endpoint with a wrong role (resulting in HTTP status code 403)
-
Calling a REST endpoint without JWT (resulting in HTTP status code 401)
-