Secure your Spring REST API using OAuth2

Published by Vignesh M on

In this post, let’s see the techniques about securing the Spring REST API using OAuth2. We will start by building an Authorization server to generate OAuth2 tokens. Then we will build our REST API which is the resource server. Finally, we will configure the security aspects for our resource server to use OAuth2. We will then test our secured REST API using Postman HTTP client.

Pre-requisite tools to build our application

  1. Java 1.8
  2. Maven
  3. Any IDE of your choice
  4. Postman HTTP client

1. Brief Intro to OAuth2 concept

Before starting our development works, let’s go through the OAuth2 concept in general. This will help us to understand the overall picture in a better way.

The OAuth 2.0 authorization framework enables a third party application to obtain limited access to HTTP service (resource). It does this by introducing an authorization layer and separating the role of the client from that of the resource owner. In OAuth, the client requests access to resources controlled by the resource owner and hosted by the resource server, and is issued a different set of credentials than those of the resource
owner.

1.1. Roles

OAuth defines four roles:

resource owner
An entity capable of granting access to a protected resource. When the resource owner is a person, it is referred to as an end-user.

resource server
The server hosting the protected resources, capable of accepting and responding to protected resource requests using access tokens.

client
An application making protected resource requests on behalf of the resource owner and with its authorization. The term “client” does not imply any particular implementation characteristics (e.g., whether the application executes on a server, a desktop, or other
devices).

authorization server
The server issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorization.

1.2 Protocol flow


The OAuth 2.0 flow illustrated in above Figure includes the following steps:

(A) The client requests an access token by authenticating with the authorization server and presenting an authorization grant [Authorization Grant - credentials representing the resource owner's authorization.].

(B) The authorization server authenticates the client and validates the authorization grant, and if valid, issues an access token and a refresh token.

(C) The client makes a protected resource request to the resource server by presenting the access token.

(D) The resource server validates the access token, and if valid, serves the request.

(E) Steps (C) and (D) repeat until the access token expires. If the client knows the access token expired, it skips to step (G); otherwise, it makes another protected resource request.

(F) Since the access token is invalid, the resource server returns an invalid token error.

(G) The client requests a new access token by authenticating with the authorization server and presenting the refresh token. The client authentication requirements are based on the client type and on the authorization server policies.

(H) The authorization server authenticates the client and validates the refresh token, and if valid, issues a new access token (and, optionally, a new refresh token).

1.3 OAuth2 Grant types

Grant types can be either of four types.

  1. Authorization code
  2. Implicit
  3. Resource owner password credentials
  4. Client credentials

We are going to use Resource owner password credentials grant type for this example.

(A) The resource owner provides the client with its username and
password.

(B) The client requests an access token from the authorization
server’s token endpoint by including the credentials received
from the resource owner. When making the request, the client
authenticates with the authorization server.

(C) The authorization server authenticates the client and validates
the resource owner credentials, and if valid, issues an access
token.

We will then use the access token in the request header when accessing the protected resource.

2. Developing the Authorization Server

Now that we have seen the OAuth2 concept, we will start developing our Authorization Server which issues access and refresh tokens to client upon successful authenticating the resource owner.

Create a  simple Maven project and add the below dependencies in the pom.xml. Alternatively, you can use spring initializr to bootstrap the application at ease.

2.1. Maven dependencies

2.2 Bootstrap class for Authorization server

The following listing shows the code for bootstrap class.

The annotation@EnableAuthorizationServer tells the Spring to expose REST endpoints which will be used in the Oauth2 process. Then with the annotation @EnableResourceServer , Spring will enable a security filter that authenticates the requests via an incoming OAuth2 token.

2.3. REST Controller for exposing oauth/user endpoint

Next, we will expose the endpoint oauth/user in the controller class. This endpoint will be used by the protected resources to validate the OAuth2 access token and to retrieve the roles associated with the user accessing the protected resource.

2.4 Register client applications with OAuth2 service

In this step, we will configure the client applications which are authorized to use our OAuth2 service. We are going to use a JDBC based token store backed by embedded H2 database for this example. Below listing shows the code for OAuth2Config.java

We have overridden two configure() methods from AuthorizationServerConfigurerAdapter class. The first one takes the parameter endpoints of type AuthorizationServerEndpointsConfigurer. In this method, we have declared the components tokenStoreauthenticationManager and userDetailsService to be used by the Spring. Also, we have registered the tokenStore bean using the dataSource reference.

The second configure method takes the parameter clients of type ClientDetailsServiceConfigurer. In this method, we are instructing the Spring to refer the client details from the H2 database by providing the necessary JDBC details. Spring will in turn query the entries in the table OAUTH_CLIENT_DETAILS to check whether the client application invoking the request is allowed to access the services protected by OAuth2 service.

You may also note that we have provided the reference of passwordEncoder. This means we have encrypted and stored some client secrets in database. Spring will then use the passwordEncoder reference to resolve the encrypted values. We have also registered the tokenServices bean using the tokenStore reference and customized the access token validity to 3600 seconds.

2.5 Configuring user details and roles

In last step, we have configured the client application details and the secrets with OAuth2 service. Next, we need to set up the application user credentials and their roles. These details can be stored in-memory, JDBC data store or in LDAP server. For our example, we are going to use JDBC data store.

Below listing shows the code for WebSecurityConfig.java which contains the configuration details.

We have registered two beans of type AuthenticationManager and UserDetailsService. Also to note, that we have used the default implementation provided by WebSecurityConfigurerAdapter class.

Then, we have overridden configure() method which takes auth as parameter of type AuthenticationManagerBuilder. In this method, we have instructed the Spring to use JDBC dataSource for getting the details needed for authenticating the users. The user credentials will be referred from USERS table and their roles will be referred from USER_ROLES table.

2.6 Schema details of OAuth2 tables for H2 database

We have used the JDBC data store in our example. Hence we need the database schema in place to have the flow working properly. Below listing shows the schema.sql for H2 database.

The data which need to be populated is in the listing below:

If the schema.sql and data.sql is in the classpath resource, then Spring will execute them when bootstrapping the application. You can notice that the credential values are encrypted and stored. For this example, to make the things simple I have used the BCryptPasswordEncoder to encrypt the values. Below listing shows the code for utility class BCryptEncoderUtil.java for your reference.

2.7 Configuring the H2 console URL path

For this example, we are using embedded H2 database. In order to access the H2 console, we have to do some configurations. First, we need the below entries to be added in application.properties

We can further customize the URL path of the H2 console by programmatic configuration as shown in below listing.

In the above listing we have registered a H2 tcp server bean on port 9092. The reason being we need to use common jdbc tokenStore for both OAuth and Resource servers. When configuring the resource server we will use this tcp port to connect to H2 database spring.datasource.url=jdbc:h2:tcp://localhost:9092/mem:bookstore

2.7 Bypass the access check for H2 console

The endpoints exposed by our OAuth2 service is protected and only the authenticated users can access it. However for this example, we can relax the rule for H2 console so that we can access it without any authentication.

To do so, we need to use the ResourceServerConfigurerAdapter to configure the access rule. The below listing shows the code for ResourceServerConfig.java

3. Develop the Resource Server

Okay! We have completed developing our Authorization server which issues access tokens for the authorized client applications. Now, we are ready to develop the resource server and to protect them using OAuth2.

We will develop a book store API where users can get the details of book using ISBN, add new book to the store. The users with the admin role will additionally have access to delete a book using ISBN.

3.1 Maven dependencies

To start with, create a simple maven project and add the below dependencies in the pom.xml.

3.2 Bootstrap class for Resource server

Below listing shows the code for resource server.

The @EnableResourceServer annotation tells the Spring that it is protected resource and will enable a filter that intercepts all the requests to check whether a valid OAuth2 access token is present in the HTTP header. The validity of the access token is determined by making a call to the callback URL defined in the property security.oauth2.resource.user-info-uri. We will configure this shortly in the next sections.

3.3 Protecting the resource via user specific role

We can configure the access control rules using the ResourceServerConfigurerAdapter class in Spring. We need to extend this class and override the configure() method. The configure method will take parameter of type HttpSecurity class which will expose necessary methods to define our access rules. Below listing shows the code for ResourceServerConfig.java

With the configuration in the above listing, we have restricted the Http DELETE operation only for users having ADMIN role. Also, if the URL contains /book-store/admin/** then it will be allowed only for ADMIN users. All other endpoints are allowed for authenticated users. For the purpose of this example, we have allowed H2 console URL to all users with out any authentication.

Also, we have used the same tokenStore we have used for OAuth2 server. This is important since the resource server has to validate the access token available in the incoming request.

3.4 Application.properties for Resource Server

The application.properties for Resource server is listed as below:

Spring will use the URI configured for property security.oauth2.resource.user-info-uri to get the details about the User accessing the protected resource from OAuth2 server.

3.5 Developing the other aspects of Resource Server

We have configured the security aspects of the resource server so far. We will now quickly complete the development of endpoints for accessing the bookstore database.

3.5.1 Controller class

3.5.2 POJO class representing the Book

This class uses Builder pattern to create new Book object.

3.5.3 Service class for processing the incoming request 

Utility class as in below listing.

3.5.4 Repository classes for performing database operations

Implementation of Row mapper class is as below:

3.5.5 And the Response builder class

4. Test the OAuth2 and Resource Server

Okay! We have completed developing our OAuth2 and Resource Server. We will test them to see how it works. Let’s make use of Postman client for this purpose.

4.1 Get the OAuth2 access token

For this we have to make a request to http://localhost:8088/oauth/token. The port number is 8088 since we have configured it in the OAuth2 server’s application.properties file.

First, we are providing the user credentials in the Authorization tab as shown in the below listing.

Next, we need to pass additional information such as grant type, scope, username and password as HTTP form parameters.

  • grant_type – Grant type is password since we are using resource owner password credentials grant for this example.
  • scope – Scope is either webclient or mobileclient. Since we have configured these two in the data.sql available in the OAuth2 server’s resources folder.
  • username – Name of the user logging in.
  • password – password of the user logging in.

Now we can send the request to OAuth2 server. The response should be as shown in the below listing and it contains below attributes.

  • access_token – The Oauth2 token that has to be provided with the each service call to the protected resource.
  • token_type – The type of the OAuth2 token being provided.
  • refresh_token – The refresh token which can be presented to OAuth2 server to reissue a access token after it has been expired.
  • expires_in – The number of seconds for which the access token is valid. We have configured it as 3600 for this example.
  • scope – The scope that this OAuth2 token is valid for.

 

4.2 Access the protected resource

We have received the access token from OAuth2 server. Now we can access the protected resource by passing the token in the request header with key value Authorization. We have configured the port for resource server as 7077. So we can now access the book from book-store API with the URL http://localhost:7077/book-store/book?isbn=978-3-598-21506-3

  • Authorization – The header contains the value of the format Bearer <access_token_from_oauth2_server>.

 

The response from API call is as shown in the below listing.

4.3 Testing the role specific access

To recollect, we have configured the Http DELETE operation only for Admin users. Let’s test this out.

Access the Delete Book operation as bookstore.user. This user doesn’t have Admin role.

Now, access the DELETE book API call as Admin user bookstore.admin. For this you need another access token from OAuth2 server by providing the username and password of Admin user. Refer the section 4.1 where we have done the same for bookstore.user.

After getting the access token for Admin credentials, provide them in the request header for DELETE operation. The response should be successful now.

The access token provided in this case is for Admin user. The response status is 204 No Content since we have configured our API to return the No Content for DELETE operation.

Conclusion

Great! We have completed our development and testing of OAuth2 and Resource server.

  • We have developed our OAuth2 server and configured it to provide access tokens.
  • Then we have protected our Resource server using OAuth2.
  • The resource server is configured to check the validity of the access token by calling the endpoint exposed by OAuth2 server(/oauth/user).
  • Also, we have configured the role specific access for the specific operations like Http DELETE.

Hope this post have helped you to understand the OAuth2 concepts by leveraging the Spring security libraries. The complete source code of this example is available in the downloads section for reference.

If you have any questions, please share it in the comments section.

Downloads

SpringSecurity_Using_OAuth2_Example.zip

References

  1. Spring Microservices in Action – chapter 7
  2. The OAuth 2.0 Authorization Framework – rfc6749


    Vignesh M

    Java developer , AWS Certified Solutions Architect Associate and Cloud technology enthusiast. Currently working for Hexaware technologies. He believes that knowledge increases by sharing not by saving.

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.