Role-Based authorization For Spring Boot using WSO2 Identity server
Authorization is an extremely common use case for any application. It defines what I can do as a person in the application. So this is provided by almost every Identity and Access Management Vendor including WSO2 Identity Server.
I hope you can remember when we create users in our computers we assign them roles. Such as Administrator, User, Guest User. These roles define the set of privileges each user has. This is what is called as role-based authorization.
Spring Boot, on the other hand, is another popular open-source Java-based framework used to create a micro Service. At some point, people needed to secure their microservices. Therefore Spring Security came in. Spring security can handle authentication and role-based access control. But not attribute-based authorization.
Let’s say I wanted to implement security for my microservices. I need to declare roles, Handle Users, Databases almost everything to get this system up and running. Still, if I needed to switch for attribute-based Authorization all my effort becomes waste. After I started working with WSO2 Identity Server I though this thing has all the capabilities that needed to achieve authorization. So we should let the Spring do the microservice deploying and let the IS handle the authorization. I googled for some time looking for examples what I found was this [1]. But that example is related to APIM. I needed a working solution without using APIM that can work with Spring Boot and WSO2 Identity Server to provide role-based authorization. So I went through several blogs and videos based on this [2][3][4][5] and implemented a solution.
First Authentication. This can be also handled in SpringBoot but as we have Identity Server lets assign that task to it. So the user will first try to access the client application as he is not authenticated he will be redirected to a login page. And he will have to provide his credentials. As we are using the WSO2 Identity Server here. we can use many authenticators to achieve this task. Also, we can use second-factor authentication kind of stuff too. Anyway, after that, you will have a JWT token.
You can change what's in there in this JWT token just by configuring the service provider. Let’s say in here we will include user roles in this token hence when decrypted it will be as follows.
{
“sub”: “admin”,
“aud”: “RsF2DE8yIYojQHvMrwLCGkLPxaYa”,
“nbf”: 1569086812,
“azp”: “RsF2DE8yIYojQHvMrwLCGkLPxaYa”,
“scope”: “openid sgc”,
“iss”: “https://localhost:9443/oauth2/token",
“groups”: [
“Internal/everyone”,
“admin”,
“Application/playground2”
],
“exp”: 1569090412,
“iat”: 1569086812,
“jti”: “0162a08c-f642–4359-b227–0dc3a3580a1a”
}
Hereunder groups, you can see that there are three roles. Now User should use this JWT to access the SpringBoot API. Then the Spring Boot Application should check its roles and what are their privileges before letting him access. But roles and users are handled by Identity Server. So Spring Boot Application has no idea of them. It only has a JWT token. So This application should extract the relevant information such as to request API and user roles then send them to Identity Server for validation. If it got validated Application should let this user access the API.
This way you can change the Authorization Policy without touching the spring boot application. So SpringBoot will only handle the API which it does best. So I created a small demo to prove this concept lets go for deploy it.
- Setup and start WSO2 Identity Server.
- Configure OAuth2/OpenID Connect Service Provider.[6]
- Select the Token Issuer as JWT.
4. In ClaimConfigurations under Service Provider Configuration add. http://wso2.org/claims/role as a requested claim.
5. Create an XACML Policy using a policy provided below. You can find the instructions here. [7]
<Policy xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" PolicyId="sample" RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable" Version="1.0">
<Target>
<AnyOf>
<AllOf>
<Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-regexp-match">
<AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">^/[a-z,A-Z,0-9]{3,100}$</AttributeValue>
<AttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id" Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="true"></AttributeDesignator>
</Match>
</AllOf>
</AnyOf>
<AnyOf>
<AllOf>
<Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
<AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">token_validation</AttributeValue>
<AttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id" Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="true"></AttributeDesignator>
</Match>
</AllOf>
</AnyOf>
<AnyOf>
<AllOf>
<Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
<AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">admin</AttributeValue>
<AttributeDesignator AttributeId="http://wso2.org/claims/role" Category="urn:oasis:names:tc:xacml:1.0:subject:access-subject" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="true"></AttributeDesignator>
</Match>
</AllOf>
</AnyOf>
</Target>
<Rule Effect="Permit" RuleId="permit_by_roles"></Rule>
</Policy>
6. Publish the created XACML policy to PDP.[8]
7. Make sure the Identity server is working on localhost and port is 9443 as the client application has the hardcoded.
8. Clone the Spring Boot application from GitHub.[9]
9. Run the Spring boot application using the following command.
mvn spring-boot:run
10. We will be using Curl as the rest-client for this demo. Execute the following command on your terminal.
curl -v
-X
POST -H
"Authorization: Basic <base64 encoded client id:client secret value>"
-k
-d
"grant_type=password&username=<username>&password=<password>"
-H
"Content-Type:application/x-www-form-urlencoded"
https://localhost:9443/oauth2/token
You can have the client id and client secret from the service provider you have created.
11. Here you will receive a JWT token. Use this token to access the spring boot application as follows.
curl -X GET http://localhost:8080/hello -H ‘Authorization: Bearer <JWT TOKEN>’
12. If everything worked perfectly you should have a hello world message as follows.
There can be better solutions than this. One drawback that I saw in this solution is that the SpringBoot Application should have to keep the admin credentials of WSO2 Identity Server to call /decsionpdp. Please let me know any other approaches that can get through that. Also, don't blame me about the code quality. This was written within one day just as a proof of concept. I hope this will help lots of people that are using SpringBoot and WSO2 Identity Server.
You can change the XACML policy as per your requirement. With the same flow, you can also perform the attribute-based authorization.
[4].https://medium.com/swlh/spring-boot-security-jwt-hello-world-example-b479e457664c
[5].https://www.youtube.com/watch?v=OvnnshmiAaA
[6].https://docs.wso2.com/display/IS580/Configuring+OAuth2-OpenID+Connect+Single-Sign-On
[7].https://docs.wso2.com/display/IS580/Creating+a+XACML+Policy#CreatingaXACMLPolicy-WritePolicyinXML
[8].https://docs.wso2.com/display/IS580/Publishing+a+XACML+Policy