How to protect your APIs with self contained access token (JWT) using WSO2 API Manager and WSO2 Identity Server
In a typical enterprise information system, there is a high chance that people will use different types of systems built by different vendors to implement certain types of functionalities. The APIs might be hosted in an API Manager developed by vendor A and the user management can be implemented using a different vendor (vendor B). In this type of a situation, one system will not be able to directly contact the other system but they want to use both systems in tandem.
Self-contained access tokens are used in these types of situations where applications can get the token from one system and use that in another system to access protected resources. In this scenario, the second system does not need to make a contact to the first system over the network to validate the user information since the token is self-contained and it has relevant details about the user. This will improve the token processing time significantly since it completely removes the network interaction.
The below figure showcases a scenario where the client application receives a JWT (self-contained token) from the WSO2 Identity Server and then use that token to consume an API protected by WSO2 API Manager.
Configure WSO2 Identity Server to issue JWT self-contained tokens
Once you download and extract the WSO2 Identity Server, you need to configure it to generate JWT tokens. Follow the steps mentioned below.
<AuthorizationContextTokenGeneration>
<Enabled>true</Enabled> <TokenGeneratorImplClass>org.wso2.carbon.identity.oauth2.authcontext.JWTTokenGenerator</TokenGeneratorImplClass> <ClaimsRetrieverImplClass>org.wso2.carbon.identity.oauth2.authcontext.DefaultClaimsRetriever</ClaimsRetrieverImplClass>
<ConsumerDialectURI>http://wso2.org/claims</ConsumerDialectURI>
<SignatureAlgorithm>SHA256withRSA</SignatureAlgorithm>
<AuthorizationContextTTL>15</AuthorizationContextTTL>
</AuthorizationContextTokenGeneration>
Note: By default, the user claims are retrieved as an array. To retrieve the claims as a string instead of an array, add the following property under the
<AuthorizationContextTokenGeneration> tag in the identity.xml file.
<UseMultiValueSeparator>false</UseMultiValueSeparator>
<EnableAudiences>true</EnableAudiences>
<!-- Comment out to add Audience values to the JWT token (id_token) -->
<Audiences> <Audience>${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/token</Audience>
</Audiences>
<IDTokenIssuerID>apim-idp</IDTokenIssuerID>
Now you can start the WSO2 Identity Server by executing the following command within the IS_HOME/bin directory.
- Open the <IS_HOME>/repository/conf/identity/identity.xml file and set the <Enabled> element (found under the <OAuth>,<AuthorizationContextTokenGeneration> elements) to true as shown in the code block below.
<AuthorizationContextTokenGeneration>
<Enabled>true</Enabled> <TokenGeneratorImplClass>org.wso2.carbon.identity.oauth2.authcontext.JWTTokenGenerator</TokenGeneratorImplClass> <ClaimsRetrieverImplClass>org.wso2.carbon.identity.oauth2.authcontext.DefaultClaimsRetriever</ClaimsRetrieverImplClass>
<ConsumerDialectURI>http://wso2.org/claims</ConsumerDialectURI>
<SignatureAlgorithm>SHA256withRSA</SignatureAlgorithm>
<AuthorizationContextTTL>15</AuthorizationContextTTL>
</AuthorizationContextTokenGeneration>
Note: By default, the user claims are retrieved as an array. To retrieve the claims as a string instead of an array, add the following property under the
<AuthorizationContextTokenGeneration> tag in the identity.xml file.
<UseMultiValueSeparator>false</UseMultiValueSeparator>
- Add the following property under <OAUTH> section to use the JWT Token Builder instead of the default Token Builder.
- Configure the “audiences” parameter as mentioned below so that the token includes information about the intended audiences who can use the generated token for authenticating the user.
<EnableAudiences>true</EnableAudiences>
<!-- Comment out to add Audience values to the JWT token (id_token) -->
<Audiences> <Audience>${carbon.protocol}://${carbon.host}:${carbon.management.port}/oauth2/token</Audience>
</Audiences>
- Configure a meaningful value to the <IDTokenIssuerID> parameter in the identity.xml file
<IDTokenIssuerID>apim-idp</IDTokenIssuerID>
Now you can start the WSO2 Identity Server by executing the following command within the IS_HOME/bin directory.
$ sh wso2server.sh
Configure WSO2 API Manager to work with JWT token issued by WSO2 Identity Server
- Since we are running both the server on the same machine, we need to change the port offset value to 1 in the carbon.xml file located in APIM_HOME/repository/conf directory
Now you can start the WSO2 API Manager by executing the following command within the APIM_HOME/bin directory.
$ sh wso2server.sh
Configure service provider within the WSO2 Identity Server
Now we need to configure a service provider who is going to get JWT tokens on behalf of the users. You can log in to the WSO2 IS management console under following URL with default admin:admin username/password pair.Go into Service Providers->Add and give a name for the service provider as depicted below and click on register.
Then configure the service provider by clicking on the edit button of the service provider you just created. Select Inbound Authentication Configuration->OAuth OpenID Connect Configuration section and click on “configure” button. Fill the callback URL with some dummy value as depicted below and click update.
That’s all you have to do in the WSO2 Identity Server. You need to take a note of client key and client secret of this service provider which is now showing under OAuth/OpenID Connect configuration section as depicted below.
Configure Identity Provider within WSO2 API Manager
Now you need to log in to the WSO2 API Manager console and configure the identity provider which is issuing the JWT tokens. In this case, it is WSO2 Identity Server. You can log in with default username/password pair using the below URL.Go into Identity-> Identity Providers and click on “Add”. You will get a window similar to below figure. You need to configure the identity provider with the below mentioned values.
When you are configuring this section, you need to give values which are compatible with the identity provider which is WSO2 Identity Server.
- Identity Provider Name - This needs to be the same value as the <IDTokenIssuerID> value you configure at the identity.xml file since this value will be the issuer ID of the JWT token. Here the value is given as “apim-idp” to match the above mentioned parameter.
- Identity Provider Public Certificate - Here you need to upload the public certificate of the WSO2 Identity Server in a pem file format. The Identity Provider Public Certificate is the public certificate belonging to the identity provider. Uploading this is necessary to authenticate the response from the identity provider. This can be any certificate. Since we are using WSO2 Identity Server as the IDP we can generate the certificate using the below mentioned commands.
To create the identity provider certificate from the wso2carbon.jks file, follow the steps below.
1. Open your Command Line interface, go to the <IS_HOME>/repository/resources/security/directory. Run the following command.
keytool -export -alias wso2carbon -file wso2.crt -keystore wso2carbon.jks -storepass wso2carbon
2. Once you run this command, the wso2.crt file is generated and can be found in the <IS_HOME>/repository/resources/security/ directory.
Click Choose File and navigate to this location in order to select and upload this file.
- Alias - You need to give the clientID (client key) of the service provider which you have configured in the WSO2 Identity Server here. This will be checked when verifying the JWT token within the WSO2 API Manager.
Now you are all set to access the API using a JWT token which is issued by WSO2 Identity Server.
Access the API using the JWT token issues by WSO2 Identity Server
Now let’s log in to the API manager publisher portal and create a sample API which connects to a simple hello world service which is running in the local machine.The created API looks similar to below.
Once you create the API, you can log into the API manager store portal and sign up as a new user (testuser) with the self-sign up option.
This username and password will be used to get the JWT token for this user.
After that, you need to log in to the store portal and subscribe to the API using the default application.
Then you need to get the client id and the client secret of the default application.
Now you are all set.
- You need to first get a JWT token from the WSO2 identity server by using the token endpoint with the password grant type. You can use the below mentioned curl command to get a JWT token
Here you need to replace the <clientID>:<clientSecret> with the relevant values of the service provider which is configured at WSO2 Identity Server. This will return the JWT token with a response similar to below.
{"access_token":"eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJqb2huZG9lQGNhcmJvbi5zdXBlciIsImF1ZCI6WyJpcGtXTnlGMWZYdTRNYlNoRTZ2YUpHTkdrRElhIl0sImF6cCI6Imlwa1dOeUYxZlh1NE1iU2hFNnZhSkdOR2tESWEiLCJpc3MiOiJhcGltLWlkcCIsImV4cCI6MTUyODM2ODEwMCwiaWF0IjoxNTI4MzY0NTAwLCJqdGkiOiIxOTQxYmY5YS1jMTJkLTQ3NjYtOTMzMi02ZTg1YTNlNzI2MTIifQ.MiAZkGcOrog6KKYs5V1zED_ojQVs0vxZyFjPVjk29CPATaAEgpmH2Rq56kHJqhE3uQk4oSgMDJzp-Zk2CNPIRJYzy8pJaeP-gEE54NvRfDe1WHZJl72AAtEz9wEIQiKxkI4ZFdMlsnqmIdv8c0_lEfU4BXpH8Uho_Vatsvklv54WLEbSvHzf3M-0dioRnBDEf7xsImkcTGEsbulcKMNw9DOQFxlGLUv7r-qJIh9NUNlf0V7vXE9lVPaBSS8YDGKsjOV-PqnMAtmF6uL4eN36vcqMT5QP0C0s3pFJdz_YxEoN8xnrEn8_UNiJlZ-IxWooRFqQxFJri7fd4hlveoAKIQ","refresh_token":"f723c75a-dd06-3b5e-99a6-b5291f3cab28","token_type":"Bearer","expires_in":3600}
- Now with this JWT token, we can call the WSO2 API Manager with the JWT grant type to get an access token. You need to copy the JWT token and use that within this request.
Here you need to replace the <clientID>:<clientSecret> with the values related to the “default application” which you have used to subscribe to the API within the store. This will return the access token which you can use to access the API. A sample response is given below.
{"access_token":"400f2a54-53d8-3146-88e3-be1bf5e7450d","refresh_token":"c2656286-449f-369f-9793-2cee9132de9f","scope":"default","token_type":"Bearer","expires_in":3600}
- You can use this access token to consume the API with the below mentioned curl request.
curl -v -H "Authorization: Bearer 400f2a54-53d8-3146-88e3-be1bf5e7450d" http://172.18.0.1:8281/jwt/1.0.0
This comment has been removed by the author.
ReplyDelete