JWT Authentication for a Spring Boot Application using Asgardeo — Part 2

Chinthaka Weerakkody
10 min readMay 15, 2023

This is the part 2 of the medium which will continue the discussions on how to protect resources exposed in a spring boot application with JWT authentication using Asgardeo as the Authorization Server. If you didn’t go through part 1 of the medium, below is the link for that.

This medium will cover the following.

  • Setup Asgardeo as the authorization server
  • Protect the resources in Spring Boot Application
  • Testing the Setup

The sample application built in the part 01 of the medium will be used to continue the discussion in this medium. Below is the link to that sample application.

Setup Asgardeo as the Authorization Server

Asgardeo is the Identity as a Service(IDaaS) provided by WSO2. It can be easily used to achieve the Customer Identity and Access Management(CIAM) requirements. Please go through the official documentation if you need to read more about Asgardeo.

You have to sign up in Asgardeo if you don’t have an account yet. During sign up process you have to select a name for your organization in asgardeo. The organization name will be referred to as <ASGARDEO_ORG> in the rest of the medium. Once the Asgardeo account is ready, the Asgardeo has to be configured to work with the Spring Boot application. To represent the your application in Asgardeo, an application has be created in Asgardeo. Follow the steps below to configure the application in Asgardeo.

Note : A JWT token is required to access a protected resource as discussed in the part 01 of this medium. A sample application provided by WSO2 will be used as the application and the JWT token obtained using that application will be used to consume the resources in resource server(Spring Boot Application).

Create Asgardeo Application

  1. Login to the Asgardeo Console.
  2. Go to Develop -> Applications
  3. Click on New Application -> Traditional Web Application
  4. Fill the form to create the application.
Traditional Web Application Creation Form

Name : Sutable Name for the Application

Protocol : Protocol to be used for the integration. Select OpenID Connect

Authorized redirect URLs: User will be redirected to this URL upon successful authentication. In this scenario, the redirect URL of sample application needs to be configured . http://localhost:8080/playground2/oauth2client

5. Click on Register

6. In the created Application, go to Protocol tab and copy the Client ID and the Client Secret of the application. This copied Client ID and Client Secret will be referred as <CLIENT_ID> and <CLIENT_SECRET> respectively in the rest of this medium.

7. In the same Protocol tab scroll down to the Access Token configurations. Select JWT as the token type and click on Update.

8. Go to the Info tab of the application and copy the Issuer and the JWKS endpoints. These endpoints are necessary to configure in the resource server (Spring Boot Application). These endpoints will be referred as <ISSUER_URI> and <JWKS_URI> respectively in the rest of the medium.

9. Next is to create a user who can login to the application. Go to Manage -> Users. Then click on Add User.

10. Fill the following information.

Add User Form — Asgardeo

Username(Email) : An email address to identify the user.
First Name : First name of the user
Last Name : Last name of the user
Select Set a password for the user option and set a password that matches the password policy of the organization.

Once the above form is filled, click on Next and then click on Close.

Setting up the Sample Application

  1. Download Apache Tomcat 8.x from here and install. Tomcat server installation location will be referred as <TOMCAT_HOME> later in this guide.
  2. Download the latest playground2 application from here.
  3. Copy the playground2.war file to the <TOMCAT_HOME>/apache-tomcat-<version>/webapps folder.
  4. Start the Tomcat server.
  5. Stop the Tomcat server and navigate to <TOMCAT_HOME>/apache-tomcat-<version>/webapps/playground2/WEB-INF/classesdirectory.
  6. Open the playground2.properties file using a text editor and fill the following.
playground2.properties file

ConsumerKey : <CLIENT_ID>
ConsumerSecret : <CLIENT_SECRET>
IdentityServerHostName : api.asgardeo.io/t/<ASGARDEO_ORG>
IdentityServerPort : Keep this empty

Then save the playground2.properties file.

Run the Sample Application and Obtain a JWT Token

Below steps will guide you to start the sample application and to obtain a JWT token.

  1. Start the Tomcat server.

2. Once the Tomcat server is successfully started, open a browser and access the sample application using http://localhost:8080/playground2/ URL.

Sample application landing page

3. Click on the Import Photos button. This will redirect you to a page with the configured parameters in the Setting up the Sample Application section above.

Page with the Parameters

4. Click on the Authorize button in the page. The user will be redirected to the Asgardeo login page. Enter the user name and password of the user created in the Step 9 of Create Asgardeo Application section above. Entering correct user name and password will redirect the browser back to sample application with the Authorization code information.

Page with Authorization Code

5. Click on the Get Access Token button in that page. This will display a page with the Access token information. This is the JWT token and it can be decoded with a JWT token decoder and check the content of the token.

Retrieved Access Token

The encoded access token retrieved will be used when invoking the protected resources of the resource server (spring boot application). <JWT_TOKEN> will be used to refer to the encoded ID token in the rest of the medium.

Protect the resources in Spring Boot Application

This section discusses on how to protect the resources in the sample market place spring boot application we develop in the part 01 of the medium. There are two APIs exposed via this market place application.

  1. Item API : To list the available items in the marketplace. This API is available for public users and therefore, doesn’t need to be protected with JWT authentication.
  2. Shopping Card API : To list the items added to the shopping cart. Shopping cart is accessible only for the logged in users and therefore, JWT authentication needs to be enforced to this API.

Follow the steps below to implement JWT authentication in the sample application.

  1. Add the Oauth2 Resource Server dependency to the project. Open the pom.xml file and add below dependency.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

2. Then open the application.properties file of the spring boot application and add the below configuration into the file.

spring.security.oauth2.resourceserver.jwt.issuer-uri=https://api.asgardeo.io/t/<ASGARDEO_ORG>/oauth2/token
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://api.asgardeo.io/t/<ASGARDEO_ORG>/oauth2/jwks

spring.security.oauth2.resourceserver.jwt.issuer-uri is the minimum configurations required to configure the Spring Boot application as an Oauth2 resource server. The resource server will derive the jwks endpoint based on this issuer. The resource server gets automatically configured to validate JWT tokens. If the resource server doesn’t support the configuration endpoints, then jwk-set-uri can be configured as well. Refer the Spring Oauth2 resource server official documentation [1] for more information.

However, in this example the JWT decoder is customized in one of the next steps and therefore, configuring the jwk-set-uri is required.

3. The default configurations of the resource server protects all the resources exposed via the spring boot application. But as per the scenario described above the shopping cart resource is the only resource to be protected. The item resource does not need to be protected. For that the default SecurityFilterChain exposed in resource server needs to be overridden as below. Create following configuration class in the spring boot application.

@Configuration
public class FilterChainConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

//Permit all the GET request to "/marketplace/item" API (JWT authentication is not enforced for this API)
//Protect all the requests to "/marketplace/shoppingcart" API (JWT authentication is enforced for this API)

http.cors()
.and()
.authorizeHttpRequests((authz) -> authz.requestMatchers(HttpMethod.GET, "/item/**")
.permitAll()
.requestMatchers("/shoppingcart/**")
.authenticated()
.anyRequest()
.authenticated())
.oauth2ResourceServer()
.jwt();
return http.build();
}
}

4. One step of JWT authentication enforced by resource server is verifying the JWT type available in the JWT token. Default configuration only supports the ‘jwt’ type. But if the JWT token returned by Asgardeo is decoded, the ‘typ’ in the JWT header is set to ‘jwt+at’. This means that the token is an access token with the form of JWT.

JWT Token Header Returned by Asgardeo

A custom JWT decoder has to be configured by extending the JWT type verifier in order to support the ‘jwt+at’ format. Create the following configuration class in the spring boot application.

@Configuration
public class JWTDecoderConfiguration {
private static final String ASGARDEO_JWT_TOKEN_TYPE = "at+jwt";

//Customize JWT type verifier to support with Asgardeo

@Bean
public JwtDecoder jwtDecoder(OAuth2ResourceServerProperties properties) {
return NimbusJwtDecoder.withJwkSetUri(properties.getJwt()
.getJwkSetUri())
.jwtProcessorCustomizer(jwtCustomizer -> {
jwtCustomizer.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType(ASGARDEO_JWT_TOKEN_TYPE)));
})
.build();
}
}

Above code sets the JwkSetUri which is configured in the application.properties file in step 02 above.

With above configurations, the respective resources are protected with Oauth2 Resource Server feature.

Testing the Sample Application

Now the implementation is being done, we can check whether the resource are protected as expected. Follow the steps below to do the verification.

  1. Start the Spring boot application.
  2. Invoke both the APIs (Item API and Shopping Cart API) without any tokens. You can use the curl commands listed below to invoke those APIs.
curl 'http://localhost:8080/marketplace/item'
curl 'http://localhost:8080/marketplace/shoppingcart'

You can observe that the Item API provides successful result with 200 status code.

*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /marketplace/item HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 0
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Mon, 15 May 2023 11:07:23 GMT
<
* Connection #0 to host localhost left intact
[{"id":1,"itemCode":"BOOK-0001","itemName":"The Perception Myth","price":670,"category":"Book"},{"id":1,"itemCode":"TOY-0010","itemName":"Remote Car","price":1500,"category":"Toys"},{"id":1,"itemCode":"TOY-0010","itemName":"Remote Car","price":1500,"category":"Toys"}]

But the Shopping Cart API rejects the request with 401 Unauthorized response code.

*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /marketplace/shoppingcart HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 401
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Set-Cookie: JSESSIONID=3F2D8DB2A34E26B3E7FFF51CE993E32C; Path=/marketplace; HttpOnly
< WWW-Authenticate: Bearer
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 0
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Length: 0
< Date: Mon, 15 May 2023 11:08:54 GMT
<
* Connection #0 to host localhost left intact

Let’s invoke the shopping card API with the Bearer token. Use below curl command for that(Replace the <JWT_TOKEN> with the JWT token obtained in the Run the Sample Application and Obtain a JWT Token section above).

curl 'http://localhost:8080/marketplace/shoppingcart' \
--header 'Authorization: Bearer <JWT_TOKEN>'

Observe that invoking the shopping cart API with Bearer token returns the shopping card information with the 200 response code.

*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /marketplace/shoppingcart HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.81.0
> Accept: */*
> Authorization: Bearer <JWT_TOKEN>
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 0
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Mon, 15 May 2023 11:31:55 GMT
<
* Connection #0 to host localhost left intact
{"cartId":12,"cartItems":[{"id":1,"itemCode":"TOY-0010","itemName":"Remote Car","price":1500,"category":"Toys"}]}

We can observe that the resources are protected as expected.

Conclusion

In this article we looked at how to protect the resources we exposed in the part 01 of this article using the Oauth2 Resource Server feature of Spring boot. We also focused on how to protect only the resources which are necessary and how to allow public access to the resources as well.

The complete source code with the protected resources can be accessed using below link.

Hope this article helped you to learn about some basic stuffs of Spring Oauth2 Resource Server feature. Thank you for following this article. See you soon with another interesting medium.

--

--