Securing microservices with JSON Web Tokens

duration 25 minutes

Prerequisites:

You’ll explore how to control user and role access to microservices with MicroProfile JSON Web Token (MicroProfile JWT).

What you’ll learn

You will add token-based authentication mechanisms to authenticate, authorize, and verify users by implementing MicroProfile JWT in the system microservice.

A JSON Web Token (JWT) is a self-contained token that is designed to securely transmit information as a JSON object. The information in this JSON object is digitally signed and can be trusted and verified by the recipient.

For microservices, a token-based authentication mechanism offers a lightweight way for security controls and security tokens to propagate user identities across different services. JSON Web Token is becoming the most common token format because it follows well-defined and known standards.

MicroProfile JWT standards define the required format of JWT for authentication and authorization. The standards also map JWT claims to various Jakarta EE container APIs and make the set of claims available through getter methods.

In this guide, the application uses JWTs to authenticate a user, allowing them to make authorized requests to a secure backend service.

You will be working with two services, a frontend service and a secure system backend service. The frontend service logs a user in, builds a JWT, and makes authorized requests to the secure system service for JVM system properties. The following diagram depicts the application that is used in this guide:

JWT frontend and system services

The user signs in to the frontend service with a username and a password, at which point a JWT is created. The frontend service then makes requests, with the JWT included, to the system backend service. The secure system service verifies the JWT to ensure that the request came from the authorized frontend service. After the JWT is validated, the information in the claims, such as the user’s role, can be trusted and used to determine which system properties the user has access to.

To learn more about JSON Web Tokens, check out the jwt.io website. If you want to learn more about how JWTs can be used for user authentication and authorization, check out the Open Liberty Single Sign-on documentation.

Getting started

The fastest way to work through this guide is to clone the Git repository and use the projects that are provided inside:

git clone https://github.com/openliberty/guide-microprofile-jwt.git
cd guide-microprofile-jwt

The start directory contains the starting project that you will build upon.

The finish directory contains the finished project that you will build.

Try what you’ll build

The finish directory contains the finished JWT security implementation for the services in the application. Try the finished application before you build your own.

To try out the application, run the following commands to navigate to the finish/frontend directory and deploy the frontend service to Open Liberty:

cd finish/frontend
mvn liberty:run

Open another command-line session and run the following commands to navigate to the finish/system directory and deploy the system service to Open Liberty:

cd finish/system
mvn liberty:run

After you see the following message in both command-line sessions, both of your services are ready:

The defaultServer server is ready to run a smarter planet.

In your browser, go to the front-end web application endpoint at http://localhost:9090/login.jsf. From here, you can log in to the application with the form-based login.

Log in with one of the following usernames and its corresponding password:

Username

Password

Role

bob

bobpwd

admin, user

alice

alicepwd

user

carl

carlpwd

user

You’re redirected to a page that displays information that the front end requested from the system service, such as the system username. If you log in as an admin user, the current OS is also shown. If you log in as a user, you instead see the You are not authorized to access this system property message. You see this message because the user role doesn’t have sufficient privileges to view current OS information.

Additionally, the groups claim of the JWT is read by the system service and requested by the front end to be displayed.

You can try accessing these services without a JWT by going to the https://localhost:8443/system/properties/os system endpoint in your browser. You get a blank screen and aren’t given access because you didn’t supply a valid JWT with the request. The following error also appears in the command-line session of the system service:

[ERROR] CWWKS5522E: The MicroProfile JWT feature cannot perform authentication because a MicroProfile JWT cannot be found in the request.

When you are done with the application, stop both the frontend and system services by pressing CTRL+C in the command-line sessions where you ran them. Alternatively, you can run the following goals from the finish directory in another command-line session:

mvn -pl system liberty:stop
mvn -pl frontend liberty:stop

Creating the secure system service

Navigate to the start directory to begin.

When you run Open Liberty in development mode, known as dev mode, the server listens for file changes and automatically recompiles and deploys your updates whenever you save a new change. Run the following commands to navigate to the frontend directory and start the frontend service in dev mode:

cd frontend
mvn liberty:dev

Open another command-line session and run the following commands to navigate to the system directory and start the system service in dev mode:

cd system
mvn liberty:dev

After you see the following message, your application server in dev mode is ready:

************************************************************************
*    Liberty is running in dev mode.

The system service provides endpoints for the frontend service to use to request system properties. This service is secure and requires a valid JWT to be included in requests that are made to it. The claims in the JWT are used to determine what properties the user has access to.

Create the secure system service.

SystemResource.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2017, 2020 IBM Corporation and others.
 4 * All rights reserved. This program and the accompanying materials
 5 * are made available under the terms of the Eclipse Public License v1.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-v10.html
 8 *
 9 * Contributors:
10 *     IBM Corporation - Initial implementation
11 *******************************************************************************/
12// end::copyright[]
13package io.openliberty.guides.system;
14
15import javax.json.JsonArray;
16import javax.enterprise.context.RequestScoped;
17import javax.inject.Inject;
18import javax.ws.rs.GET;
19import javax.ws.rs.Path;
20import javax.ws.rs.Produces;
21import javax.ws.rs.core.MediaType;
22import javax.annotation.security.RolesAllowed;
23
24import org.eclipse.microprofile.jwt.Claim;
25
26@RequestScoped
27@Path("/properties")
28public class SystemResource {
29
30    @Inject
31    // tag::claim[]
32    @Claim("groups")
33    // end::claim[]
34    // tag::rolesArray[]
35    private JsonArray roles;
36    // end::rolesArray[]
37
38    @GET
39    // tag::usernameEndpoint[]
40    @Path("/username")
41    // end::usernameEndpoint[]
42    @Produces(MediaType.APPLICATION_JSON)
43    // tag::rolesAllowedAdminUser1[]
44    @RolesAllowed({ "admin", "user" })
45    // end::rolesAllowedAdminUser1[]
46    public String getUsername() {
47        return System.getProperties().getProperty("user.name");
48    }
49
50    @GET
51    // tag::osEndpoint[]
52    @Path("/os")
53    // end::osEndpoint[]
54    @Produces(MediaType.APPLICATION_JSON)
55    // tag::rolesAllowedAdmin[]
56    @RolesAllowed({ "admin" })
57    // end::rolesAllowedAdmin[]
58    public String getOS() {
59        return System.getProperties().getProperty("os.name");
60    }
61
62    @GET
63    // tag::rolesEndpoint[]
64    @Path("/jwtroles")
65    // end::rolesEndpoint[]
66    @Produces(MediaType.APPLICATION_JSON)
67    // tag::rolesAllowedAdminUser2[]
68    @RolesAllowed({ "admin", "user" })
69    // end::rolesAllowedAdminUser2[]
70    public String getRoles() {
71        return roles.toString();
72    }
73}
Create the SystemResource class.
system/src/main/java/io/openliberty/guides/system/SystemResource.java

This class has role-based access control. The role names that are used in the @RolesAllowed annotations are mapped to group names in the groups claim of the JWT, which results in an authorization decision wherever the security constraint is applied.

The /username endpoint returns the system’s username and is annotated with the @RolesAllowed({"admin, "user"}) annotation. Only authenticated users with the role of admin or user can access this endpoint.

The /os endpoint returns the system’s current OS. Here, the @RolesAllowed annotation is limited to admin, meaning that only authenticated users with the role of admin are able to access the endpoint.

While the @RolesAllowed annotation automatically reads from the groups claim of the JWT to make an authorization decision, you can also manually access the claims of the JWT by using the @Claim annotation. In this case, the groups claim is injected into the roles JSON array. The roles that are parsed from the groups claim of the JWT are then exposed back to the front end at the /jwtroles endpoint. To read more about different claims and ways to access them, check out the MicroProfile JWT documentation.

Creating a client to access the secure system service

SystemClient.java

 1//tag::copyright[]
 2/*******************************************************************************
 3* Copyright (c) 2020 IBM Corporation and others.
 4* All rights reserved. This program and the accompanying materials
 5* are made available under the terms of the Eclipse Public License v1.0
 6* which accompanies this distribution, and is available at
 7* http://www.eclipse.org/legal/epl-v10.html
 8*
 9* Contributors:
10*     IBM Corporation - initial API and implementation
11*******************************************************************************/
12// end::copyright[]
13package io.openliberty.guides.frontend.client;
14
15import javax.enterprise.context.RequestScoped;
16import javax.ws.rs.GET;
17import javax.ws.rs.Path;
18import javax.ws.rs.Produces;
19import javax.ws.rs.core.MediaType;
20import javax.ws.rs.HeaderParam;
21
22import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
23
24// tag::systemClient[]
25@RegisterRestClient(baseUri = "https://localhost:8443/system")
26@Path("/properties")
27@RequestScoped
28public interface SystemClient extends AutoCloseable{
29
30    @GET
31    @Path("/os")
32    @Produces(MediaType.APPLICATION_JSON)
33    // tag::headerParam1[]
34    public String getOS(@HeaderParam("Authorization") String authHeader);
35    // end::headerParam1[]
36
37    @GET
38    @Path("/username")
39    @Produces(MediaType.APPLICATION_JSON)
40    // tag::headerParam2[]
41    public String getUsername(@HeaderParam("Authorization") String authHeader);
42    // end::headerParam2[]
43
44    @GET
45    @Path("/jwtroles")
46    @Produces(MediaType.APPLICATION_JSON)
47    // tag::headerParam3[]
48    public String getJwtRoles(@HeaderParam("Authorization") String authHeader);
49    // end::headerParam3[]
50}
51// end::systemClient[]

ApplicationBean.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2020 IBM Corporation and others.
 4 * All rights reserved. This program and the accompanying materials
 5 * are made available under the terms of the Eclipse Public License v1.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-v10.html
 8 *
 9 * Contributors:
10 *     IBM Corporation - Initial implementation
11 *******************************************************************************/
12// end::copyright[]
13package io.openliberty.guides.frontend;
14
15import javax.enterprise.context.ApplicationScoped;
16import javax.inject.Inject;
17import javax.inject.Named;
18
19import org.eclipse.microprofile.rest.client.inject.RestClient;
20
21import io.openliberty.guides.frontend.client.SystemClient;
22import io.openliberty.guides.frontend.util.SessionUtils;
23
24
25@ApplicationScoped
26@Named
27public class ApplicationBean {
28
29    // tag::restClient[]
30    @Inject
31    @RestClient
32    private SystemClient defaultRestClient;
33    // end::restClient[]
34
35    // tag::getJwt[]
36    public String getJwt() {
37        String jwtTokenString = SessionUtils.getJwtToken();
38        String authHeader = "Bearer " + jwtTokenString;
39        return authHeader;
40    }
41    // end::getJwt[]
42
43    // tag::getOs[]
44    public String getOs() {
45        String authHeader = getJwt();
46        String os;
47        try {
48            // tag::authHeader1[]
49            os = defaultRestClient.getOS(authHeader);
50            // end::authHeader1[]
51        } catch(Exception e) {
52            return "You are not authorized to access this system property";
53        }
54        return os;
55    }
56    // end::getOs[]
57
58    // tag::getUsername[]
59    public String getUsername() {
60        String authHeader = getJwt();
61        // tag::authHeader2[]
62        return defaultRestClient.getUsername(authHeader);
63        // end::authHeader2[]
64    }
65    // end::getUsername[]
66
67    // tag::getJwtRoles[]
68    public String getJwtRoles() {
69        String authHeader = getJwt();
70        // tag::authHeader3[]
71        return defaultRestClient.getJwtRoles(authHeader);
72        // end::authHeader3[]
73    }
74    // end::getJwtRoles[]
75
76}

LoginBean.java

  1// tag::copyright[]
  2/*******************************************************************************
  3 * Copyright (c) 2018, 2020 IBM Corporation and others.
  4 * All rights reserved. This program and the accompanying materials
  5 * are made available under the terms of the Eclipse Public License v1.0
  6 * which accompanies this distribution, and is available at
  7 * http://www.eclipse.org/legal/epl-v10.html
  8 *
  9 * Contributors:
 10 *     IBM Corporation - Initial implementation
 11 *******************************************************************************/
 12// end::copyright[]
 13package io.openliberty.guides.frontend;
 14
 15import java.util.Set;
 16import java.util.HashSet;
 17import javax.servlet.ServletException;
 18import javax.servlet.http.HttpServletRequest;
 19import javax.servlet.http.HttpSession;
 20import javax.enterprise.context.ApplicationScoped;
 21import javax.inject.Named;
 22
 23import com.ibm.websphere.security.jwt.JwtBuilder;
 24import com.ibm.websphere.security.jwt.Claims;
 25
 26import io.openliberty.guides.frontend.util.SessionUtils;
 27
 28// tag::loginBean[]
 29@ApplicationScoped
 30@Named
 31public class LoginBean {
 32
 33    private String username;
 34    private String password;
 35
 36    public void setUsername(String username) {
 37        this.username = username;
 38    }
 39
 40    public void setPassword(String password) {
 41        this.password = password;
 42    }
 43
 44    public String getUsername() {
 45        return username;
 46    }
 47
 48    public String getPassword() {
 49        return password;
 50    }
 51
 52    // tag::doLogin[]
 53    public String doLogin() throws Exception {
 54        HttpServletRequest request = SessionUtils.getRequest();
 55
 56        try {
 57            request.logout();
 58            request.login(this.username, this.password);
 59        } catch (ServletException e) {
 60            System.out.println("Login failed.");
 61            return "error.jsf";
 62        }
 63
 64        String remoteUser = request.getRemoteUser();
 65        Set<String> roles = getRoles(request);
 66        if (remoteUser != null && remoteUser.equals(username)){
 67            String jwt = buildJwt(username, roles);
 68            HttpSession ses = request.getSession();
 69            if (ses == null) {
 70                System.out.println("Session timed out.");
 71            } else {
 72                // tag::setAttribute[]
 73                ses.setAttribute("jwt", jwt);
 74                // end::setAttribute[]
 75            }
 76        } else {
 77            System.out.println("Failed to update JWT in session.");
 78        }
 79        return "application.jsf?faces-redirect=true";
 80    }
 81    // end::doLogin[]
 82    // tag::buildJwt[]
 83
 84  private String buildJwt(String userName, Set<String> roles) throws Exception {
 85        // tag::jwtBuilder[]
 86        return JwtBuilder.create("jwtFrontEndBuilder")
 87        // end::jwtBuilder[]
 88                         .claim(Claims.SUBJECT, userName)
 89                         .claim("upn", userName)
 90                         // tag::groups[]
 91                         .claim("groups", roles.toArray(new String[roles.size()]))
 92                         // end::groups[]
 93                         .buildJwt()
 94                         .compact();
 95
 96    }
 97    // end::buildJwt[]
 98
 99    private Set<String> getRoles(HttpServletRequest request) {
100        Set<String> roles = new HashSet<String>();
101        boolean isAdmin = request.isUserInRole("admin");
102        boolean isUser = request.isUserInRole("user");
103        if (isAdmin) { roles.add("admin");}
104        if (isUser) { roles.add("user");}
105        return roles;
106    }
107}
108// end::loginBean[]

Create a RESTful client interface for the frontend service.

Create the SystemClient class.
frontend/src/main/java/io/openliberty/guides/frontend/client/SystemClient.java

This interface declares methods for accessing each of the endpoints that were previously set up in the system service.

The MicroProfile Rest Client feature automatically builds and generates a client implementation based on what is defined in the SystemClient interface. You don’t need to set up the client and connect with the remote service.

As discussed, the system service is secured and requests made to it must include a valid JWT in the Authorization header. The @HeaderParam annotations include the JWT by specifying that the value of the String authHeader parameter, which contains the JWT, be used as the value for the Authorization header. This header is included in all of the requests that are made to the system service through this client.

Create the application bean that the front-end UI uses to request data.

Create the ApplicationBean class.
frontend/src/main/java/io/openliberty/guides/frontend/ApplicationBean.java

The application bean is used to populate the table in the front end by making requests for data through the defaultRestClient, which is an injected instance of the SystemClient class that you created. The getOs(), getUsername(), and getJwtRoles() methods call their associated methods of the SystemClient class with the authHeader passed in as a parameter. The authHeader is a string that consists of the JWT with Bearer prefixed to it. The authHeader is included in the Authorization header of the subsequent requests that are made by the defaultRestClient instance.

The JWT for these requests is retrieved from the session attributes with the getJwt() method. The JWT is stored in the session attributes by the provided LoginBean class. When the user logs in to the front end, the doLogin() method is called and builds the JWT. Then, the setAttribute() method stores it as an HttpSession attribute. The JWT is built by using the JwtBuilder APIs in the buildJwt() method. You can see that the claim() method is being used to set the groups claim of the token. This claim is used to provide the role-based access that you implemented.

Configuring MicroProfile JWT

microprofile-config.properties

1# tag::issuer[]
2mp.jwt.verify.issuer=http://openliberty.io
3# end::issuer[]

server.xml

 1<server description="Sample Liberty server">
 2
 3  <featureManager>
 4    <feature>jaxrs-2.1</feature>
 5    <feature>jsonp-1.1</feature>
 6    <feature>cdi-2.0</feature>
 7    <feature>mpConfig-1.4</feature>
 8    <feature>mpRestClient-1.4</feature>
 9    <feature>appSecurity-3.0</feature>
10    <feature>servlet-4.0</feature>
11    <!-- tag::mpJwt[] -->
12    <feature>mpJwt-1.1</feature>
13    <!-- end::mpJwt[] -->
14  </featureManager>
15
16  <variable name="default.http.port" defaultValue="8080"/>
17  <variable name="default.https.port" defaultValue="8443"/>
18
19  <keyStore id="defaultKeyStore" password="secret"/>
20
21  <httpEndpoint host="*" httpPort="${default.http.port}" httpsPort="${default.https.port}"
22                id="defaultHttpEndpoint"/>
23
24  <webApplication location="system.war" contextRoot="/"/>
25
26</server>

Configure the mpJwt feature in the microprofile-config.properties file for the system service.

Create the microprofile-config.properties file.
system/src/main/webapp/META-INF/microprofile-config.properties

The mp.jwt.verify.issuer config property specifies the expected value of the issuer claim on an incoming JWT. Incoming JWTs with an issuer claim that’s different from this expected value aren’t considered valid.

Next, add the MicroProfile JSON Web Token feature to the server configuration file for the system service.

Replace the system server configuration file.
system/src/main/liberty/config/server.xml

The mpJwt feature adds the libraries that are required for MicroProfile JWT implementation.

Building and running the application

Because you are running the frontend and system services in dev mode, the changes that you made were automatically picked up. You’re now ready to check out your application in your browser.

In your browser, go to the front-end web application endpoint at http://localhost:9090/login.jsf. Log in with one of the following usernames and its corresponding password:

Username

Password

Role

bob

bobpwd

admin, user

alice

alicepwd

user

carl

carlpwd

user

After you log in, you can see the information that’s retrieved from the system service. With successfully implemented role-based access in the application, if you log in as a user role, you don’t have access to the OS property.

You can also see the value of the groups claim in the row with the Roles: label. These roles are read from the JWT and sent back to the front end to be displayed.

You can check that the system service is secured against unauthenticated requests by going to the https://localhost:8443/system/properties/os system endpoint in your browser.

In the front end, you see your JWT displayed in the row with the JSON Web Token label.

To see the specific information that this JWT holds, you can enter it into the token reader on the JWT.io website. The token reader shows you the header, which contains information about the JWT, as shown in the following example:

{
  "kid": "NPzyG3ZMzljUwQgbzi44",
  "typ": "JWT",
  "alg": "RS256"
}

The token reader also shows you the payload, which contains the claims information:

{
  "token_type": "Bearer",
  "sub": "bob",
  "upn": "bob",
  "groups": [ "admin", "user" ],
  "iss": "http://openliberty.io",
  "exp": 1596723489,
  "iat": 1596637089
}

You can learn more about these claims in the MicroProfile JWT documentation.

Testing the application

SystemEndpointIT.java

  1//tag::copyright[]
  2/*******************************************************************************
  3* Copyright (c) 2020 IBM Corporation and others.
  4* All rights reserved. This program and the accompanying materials
  5* are made available under the terms of the Eclipse Public License v1.0
  6* which accompanies this distribution, and is available at
  7* http://www.eclipse.org/legal/epl-v10.html
  8*
  9* Contributors:
 10*     IBM Corporation - initial API and implementation
 11*******************************************************************************/
 12// end::copyright[]
 13package it.io.openliberty.guides.system;
 14
 15import javax.ws.rs.client.Client;
 16import javax.ws.rs.client.ClientBuilder;
 17import javax.ws.rs.client.Invocation.Builder;
 18import javax.ws.rs.core.Response;
 19import javax.ws.rs.core.HttpHeaders;
 20import javax.ws.rs.core.MediaType;
 21
 22import org.junit.jupiter.api.Test;
 23import org.junit.jupiter.api.BeforeAll;
 24import static org.junit.jupiter.api.Assertions.assertEquals;
 25
 26import it.io.openliberty.guides.system.util.JwtBuilder;
 27
 28public class SystemEndpointIT {
 29
 30    static String authHeaderAdmin;
 31    static String authHeaderUser;
 32    static String urlOS;
 33    static String urlUsername;
 34    static String urlRoles;
 35
 36    @BeforeAll
 37    private static void setup() throws Exception{
 38        String urlBase = "http://" + System.getProperty("hostname")
 39                 + ":" + System.getProperty("http.port")
 40                 + "/system/properties";
 41        urlOS = urlBase + "/os";
 42        urlUsername = urlBase + "/username";
 43        urlRoles = urlBase + "/jwtroles";
 44
 45        authHeaderAdmin = "Bearer " + new JwtBuilder().createAdminJwt("testUser");
 46        authHeaderUser = "Bearer " + new JwtBuilder().createUserJwt("testUser");
 47    }
 48
 49    @Test
 50    // tag::os[]
 51    public void testOSEndpoint() {
 52        // tag::adminRequest1[]
 53        Response response = makeRequest(urlOS, authHeaderAdmin);
 54        // end::adminRequest1[]
 55        assertEquals(200, response.getStatus(), "Incorrect response code from " + urlOS);
 56        assertEquals(System.getProperty("os.name"), response.readEntity(String.class),
 57                "The system property for the local and remote JVM should match");
 58
 59        // tag::userRequest1[]
 60        response = makeRequest(urlOS, authHeaderUser);
 61        // end::userRequest1[]
 62        assertEquals(403, response.getStatus(), "Incorrect response code from " + urlOS);
 63
 64        // tag::nojwtRequest1[]
 65        response = makeRequest(urlOS, null);
 66        // end::nojwtRequest1[]
 67        assertEquals(401, response.getStatus(), "Incorrect response code from " + urlOS);
 68
 69        response.close();
 70    }
 71    // end::os[]
 72
 73    @Test
 74    // tag::username[]
 75    public void testUsernameEndpoint() {
 76        // tag::adminRequest2[]
 77        Response response = makeRequest(urlUsername, authHeaderAdmin);
 78        // end::adminRequest2[]
 79        assertEquals(200, response.getStatus(),
 80                "Incorrect response code from " + urlUsername);
 81
 82        // tag::userRequest2[]
 83        response = makeRequest(urlUsername, authHeaderUser);
 84        // end::userRequest2[]
 85        assertEquals(200, response.getStatus(),
 86                "Incorrect response code from " + urlUsername);
 87
 88        // tag::nojwtRequest2[]
 89        response = makeRequest(urlUsername, null);
 90        // end::nojwtRequest2[]
 91        assertEquals(401, response.getStatus(),
 92                "Incorrect response code from " + urlUsername);
 93
 94        response.close();
 95    }
 96    // end::username[]
 97
 98    @Test
 99    // tag::roles[]
100    public void testRolesEndpoint() {
101        // tag::adminRequest3[]
102        Response response = makeRequest(urlRoles, authHeaderAdmin);
103        // end::adminRequest3[]
104        assertEquals(200, response.getStatus(),
105                "Incorrect response code from " + urlRoles);
106        assertEquals("[\"admin\",\"user\"]", response.readEntity(String.class),
107                "Incorrect groups claim in token " + urlRoles);
108
109        // tag::userRequest3[]
110        response = makeRequest(urlRoles, authHeaderUser);
111        // end::userRequest3[]
112        assertEquals(200, response.getStatus(),
113                "Incorrect response code from " + urlRoles);
114        assertEquals("[\"user\"]", response.readEntity(String.class),
115                "Incorrect groups claim in token " + urlRoles);
116
117        // tag::nojwtRequest3[]
118        response = makeRequest(urlRoles, null);
119        // end::nojwtRequest3[]
120        assertEquals(401, response.getStatus(),
121                "Incorrect response code from " + urlRoles);
122
123        response.close();
124    }
125    // end::roles[]
126
127    private Response makeRequest(String url, String authHeader) {
128        Client client = ClientBuilder.newClient();
129        Builder builder = client.target(url).request();
130        builder.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
131        if (authHeader != null) {
132            builder.header(HttpHeaders.AUTHORIZATION, authHeader);
133        }
134        Response response = builder.get();
135        return response;
136    }
137
138}

You can manually check that the system service is secure by making requests to each of the endpoints with and without valid JWTs. However, automated tests are a much better approach because they are more reliable and trigger a failure if a breaking change is introduced.

Create the SystemEndpointIT class.
system/src/test/java/it/io/openliberty/guides/system/SystemEndpointIT.java

The testOSEndpoint(), testUsernameEndpoint(), and testRolesEndpoint() tests test the /os, /username, and /roles endpoints.

Each test makes three requests to its associated endpoint. The first makeRequest() call has a JWT with the admin role. The second makeRequest() call has a JWT with the user role. The third makeRequest() call has no JWT at all. The responses to these requests are checked based on the role-based access rules for the endpoints. The admin requests should be successful on all endpoints. The user requests should be denied by the /os endpoint but successfully access the /username and /jwtroles endpoints. The requests that don’t include a JWT should be denied access to all endpoints.

Running the tests

Because you started Open Liberty in dev mode, press the enter/return key from the command-line session of the system service to run the tests. You see the following output:

-------------------------------------------------------
  T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.system.SystemEndpointIT
[ERROR   ] CWWKS5522E: The MicroProfile JWT feature cannot perform authentication because a MicroProfile JWT cannot be found in the request.
[ERROR   ] CWWKS5522E: The MicroProfile JWT feature cannot perform authentication because a MicroProfile JWT cannot be found in the request.
[ERROR   ] CWWKS5522E: The MicroProfile JWT feature cannot perform authentication because a MicroProfile JWT cannot be found in the request.
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.648 s - in it.io.openliberty.guides.system.SystemEndpointIT

Results:

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0

The three errors in the output are expected and result from the system service successfully rejecting the requests that didn’t include a JWT.

When you are finished testing the application, stop both the frontend and system services by pressing CTRL+C in the command-line sessions where you ran them. Alternatively, you can run the following goals from the start directory in another command-line session:

mvn -pl system liberty:stop
mvn -pl frontend liberty:stop

Great work! You’re done!

You learned how to use MicroProfile JWT to validate JWTs, authenticate and authorize users to secure your microservices in Open Liberty.

Guide Attribution

Securing microservices with JSON Web Tokens by Open Liberty is licensed under CC BY-ND 4.0

Copied to clipboard
Copy code block
Copy file contents

Prerequisites:

Nice work! Where to next?

What did you think of this guide?

Extreme Dislike Dislike Like Extreme Like

What could make this guide better?

Raise an issue to share feedback

Create a pull request to contribute to this guide

Need help?

Ask a question on Stack Overflow

Like Open Liberty? Star our repo on GitHub.

Star