Preloader image

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:

mvn clean test

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)