Spring Security 5: OAuth 2.0 Resource Server with JWT
Development team in any organisation starts with API development using plethora of languages and framework available like Spring Boot, Flask, Express and others.
One important thing in terms of complete API Journey is securing our APIs and here comes Spring Security to the rescue.
Ideally, we should take API Security into consideration start from the development.
What is Spring Security?
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
Spring Security is a framework that focuses on providing both authentication and authorization to Java applications.
Spring Security has support for OAuth 2.0 for the following:
OAuth 2.0 Login
The OAuth 2.0 Login feature provides an application with the capability to have users log in to the application by using their existing account at an OAuth 2.0 Provider (e.g. GitHub) or OpenID Connect 1.0 Provider (such as Google).
OAuth 2.0 Resource Server
As a Resource Server, Spring Security 5 provides support for protecting the API endpoints using two forms for OAuth2.0 Bearer Tokens – JWT and Opaque.
OAuth 2.0 Client
The OAuth 2.0 Client features provide support for the Client role as defined in the OAuth 2.0 Authorization Framework.
Spring Security OAuth 2.0 Resource Server
In this story, we will see how we can make use of Spring Security 5 OAuth 2.0 support as resource server to protect the endpoints using JWT as Bearer Token.
Pre-requisites for code along with this tutorial
To gain the concepts from this tutorial, you will be needing the following setup ( no one is stopping from you to read though. So in case you don’t have the setup ready, you can read this tutorial and do the hands-on later).
- IDE of your choice
- OAuth 2.0 Authorisation server to issue token in JWT format
You can go through the below Story on Getting Started with Keycloak. It will take only a moment to setup in your local development environment.
Setting up OAuth2.0/ OpenID Client in Keycloak
Once you are logged into Keycloak and able to see Master Realm as shown in the below screenshot.
You need to go to Clients Tab to create a new Client.
Once you click create client, you will see below screen. Enter the name for your Client ID (you can give UUID as well here so that your client id is not obvious in different OAuth 2.0 flows)
Once we click on save, OAuth client will be created in keycloak and the flow will be navigated to next screen where the option to select Name of Client, Client Access Type and other fields will appear.
Here you need to make few updates:
- Select Access Type as “confidential”
- Service Account Enabled as ON for OAuth 2.0 client_credentials grant_type.
Generating JWT Access Token
First you need to get the Access Token URL and OpenID provides a discovery endpoint which is also known as well-known openid configuration.
For Keycloak Master realm here is the well known configuration url. In case you are using any other realm, just replace master with the “realm name” that you are on.
http://localhost:8080/realms/master/.well-known/openid-configuration
To generate Access Token, you can use the below curl command by replacing the “Authorizaton” Header value as Base64(client_id:client_secret) as per the Client you have created.
curl -X POST \
http://localhost:8080/realms/master/protocol/openid-connect/token \
-H 'Authorization: Basic c3ByaW5nLXNlY3VyaXR5LWp3dC1kZW1vOmY5Yjk1YzAyLTA1YWYtNGNjMi05MjA3LTNhMWZkMzlkOWU0MQ==' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Postman-Token: 49d57263-5f46-4a33-bd85-18f10b608165' \
-H 'cache-control: no-cache' \
-d 'grant_type=client_credentials'
Response will look like following from the token endpoint.
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJGZHQyVl84bEdHZERrcHFSRlp4WkNyRFFCMWF6clBsRWZOOGlTa1dWQVpRIn0.eyJleHAiOjE2MjM4NjkwNTIsImlhdCI6MTYyMzg2ODk5MiwianRpIjoiNzE3YWVmYTMtZjUyNS00YjNhLTkxMzgtM2EwNzQ0YThkODE5IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOlsiZGVtby1jbGllbnQiLCJhY2NvdW50Il0sInN1YiI6IjVmN2I1NzJhLTVkZDktNDRhYy1hNGZlLWVjNDczYjNhOTA0NiIsInR5cCI6IkJlYXJlciIsImF6cCI6InNwcmluZy1zZWN1cml0eS1qd3QtZGVtbyIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1tYXN0ZXIiLCJvZmZsaW5lX2FjY2VzcyIsIkRlbW9BcHBSb2xlIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJkZW1vLWNsaWVudCI6eyJyb2xlcyI6WyJ1bWFfcHJvdGVjdGlvbiJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJjbGllbnRIb3N0IjoiMTI3LjAuMC4xIiwiY2xpZW50SWQiOiJzcHJpbmctc2VjdXJpdHktand0LWRlbW8iLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzZXJ2aWNlLWFjY291bnQtc3ByaW5nLXNlY3VyaXR5LWp3dC1kZW1vIiwiY2xpZW50QWRkcmVzcyI6IjEyNy4wLjAuMSJ9.NEY9mLwTi7TjmLrtRQz9zKCXcGmNUMZrlhwgHhOTvFlKTE3PepSLJqS9KVyxMwIQkT-a3Vqp2cHD-0xP-RGPF6z4zEWOD37WWUc94kSkY7sHLTDHfDJa9fLYbXSqWpdv1PrqVgzzbRK-8Rspk6Q2V8z_UE3vpWce2RvYHB4v5HcH6B7_JMNPQBd4yu3XM_NisPQOPMB_GuSwZYkbnKAZnQepxoSLiOBQ7BOYx0M7-Jf7ql6rRDL4TBy6KYqBnXkQ2xaSc28aC9kOWiNuHMTLTyB5y0ocDCrkdpPDUOeAasIIsKY5za-MVbaS1Yq4S3Fhpm6XjOC4qCOL5rbnbphwzw",
"expires_in": 60,
"refresh_expires_in": 0,
"token_type": "Bearer",
"not-before-policy": 0,
"scope": "profile email"
}
By doing all the Keycloak related steps, we are done with the JWT Bearer Access Token.
Minimal Configuration to secure our APIs with JWT Bearer Token
Here is the pom.xml with which you can get started with minimal configuration. Dependencies to note here are “spring-boot-starter-oauth2-resource-server” for enabling OAuth 2.0 Resource server and “spring-security-oauth2-jose” for all the JWT handling.
Now, you need to tell the OAuth 2.0 resource server where it should look for the validation of the JWT. This is done by putting the following in application.properties file.
Add a RestController to create a Hello GET API.
With this setup we are good to go.
Test the security setup we have done
Step 1: Invoke GET /hello API without passing any token. This will give 401 Unauthorized as we have not passed any token
Step 2: Generate a token as mentioned in the Token Generation Step.
Step 3: Set the Authorization type as Bearer and provide the JWT access token got from Step 2.
Here you can see that by setting Authorization type as Bearer and passing a valid JWT Token, API invocation is successful.
Also, notice that since we have provided “JwtAuthenticationToken” as argument in the RestController. So, once we have successful Token Validation, Spring Security will set the JWTAuthenticationToken object with which you can access all the JWT Attributes, Principal and other details. Do checkout all the options by playing around in the controller.
That is all for this story. Happy Reading :)
In case you are new to Spring Boot, here is YT playlist to get you started quickly.