Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

How to Protect Your APIs With Self-Contained Access Tokens (JWT)

DZone's Guide to

How to Protect Your APIs With Self-Contained Access Tokens (JWT)

APIs and JWTs are two of the hottest acronyms in tech right now. Learn how to combine them to add extra security to your application.

· Security Zone ·
Free Resource

DON’T STRESS! Assess your OSS. Get your free code scanner from FlexeraFlexNet Code Aware scans Java, NuGet, and NPM packages.

In a typical enterprise information system, there is a good 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 the user will 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 it in another system to access protected resources. In this scenario, the second system does not need to make contact with 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 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 uses that token to consume an API protected by WSO2 API Manager.

To implement the above use case, first, we need to download WSO2 Identity Server and the WSO2 API Manager from the WSO2 web site.

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.

  • Open the <IS_HOME>/repository/conf/identity/identity.xml file and set the <Enabled> element (found under the <OAuth> and <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 the <OAUTH> section to use the JWT Token Builder instead of the default Token Builder.
<IdentityOAuthTokenGenerator>org.wso2.carbon.identity.oauth2.token.JWTTokenIssuer</IdentityOAuthTokenGenerator>
  • 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 for 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 in the IS_HOME/bin directory.

$ sh wso2server.sh

Configure WSO2 API Manager to Work With JWT Tokens Issued by WSO2 Identity Server

Since we are running both the servers 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.

<Offset>1</Offset>

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 tje admin:admin username/password pair.

https://localhost:9443/carbon

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 the Inbound Authentication Configuration->OAuth OpenID Connect Configuration section and click on the “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 the client key and client secret for this service provider which is now showing under the OAuth/OpenID Connect configuration section, as depicted below.

Configure Identity Provider Within the 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 the default username/password pair using the below URL.

https://localhost:9444/carbon

Go into Identity-> Identity Providers and click on “Add.” You will get a window similar to the 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 the 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 the 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 the 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 on the local machine.

https://localhost:9444/publisher

The created API looks similar to the screenshot 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
curl -u <clientID>:<clientSecret> -k -d "grant_type=password&username=testuser&password=testuser" -H "Content-Type:application/x-www-form-urlencoded" https://localhost:9443/oauth2/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 what I've got 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.

curl -i -X POST -u <clientID>:<clientSecret> -k -d 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJqb2huZG9lQGNhcmJvbi5zdXBlciIsImF1ZCI6WyJpcGtXTnlGMWZYdTRNYlNoRTZ2YUpHTkdrRElhIl0sImF6cCI6Imlwa1dOeUYxZlh1NE1iU2hFNnZhSkdOR2tESWEiLCJpc3MiOiJhcGltLWlkcCIsImV4cCI6MTUyODM2ODEwMCwiaWF0IjoxNTI4MzY0NTAwLCJqdGkiOiIxOTQxYmY5YS1jMTJkLTQ3NjYtOTMzMi02ZTg1YTNlNzI2MTIifQ.MiAZkGcOrog6KKYs5V1zED_ojQVs0vxZyFjPVjk29CPATaAEgpmH2Rq56kHJqhE3uQk4oSgMDJzp-Zk2CNPIRJYzy8pJaeP-gEE54NvRfDe1WHZJl72AAtEz9wEIQiKxkI4ZFdMlsnqmIdv8c0_lEfU4BXpH8Uho_Vatsvklv54WLEbSvHzf3M-0dioRnBDEf7xsImkcTGEsbulcKMNw9DOQFxlGLUv7r-qJIh9NUNlf0V7vXE9lVPaBSS8YDGKsjOV-PqnMAtmF6uL4eN36vcqMT5QP0C0s3pFJdz_YxEoN8xnrEn8_UNiJlZ-IxWooRFqQxFJri7fd4hlveoAKIQ' -H 'Content-Type: application/x-www-form-urlencoded' https://localhost:9444/oauth2/token

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

Try FlexNet Code Aware Today! A free scan tool for developers. Scan Java, NuGet, and NPM packages for open source security and license compliance issues.

Topics:
security ,api security ,json web tokens ,appsec

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}