Try out JWT Bearer Grant with WSO2 Identity Server

Piraveena Paralogarajah
6 min readSep 21, 2019

--

The JSON Web Token (JWT) is simply a JSON string containing claim values. To know about JWT, please read my previous blog. A JWT will look like

header.payload.signature.

Eg:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.POstGetfAytaZS82wHcjoTyoqhMyxXiWdR7Nn7A29DNSl0EiXLdwJ6xC6AfgZWF1bOsS_TuYI3OG85AmiExREkrS6tDfTQ2B3WXlrr-wp5AokiRbz3_oB4OxG-W9KcEEbDRcZc0nH3L7LzYptiy1PtAylQGxHTWZXtGz4ht0bAecBgmpdgXMguEIcoqPJ1n3pIWk_dUZegpqx0Lka21H6XxUTxiy8OcaarA8zdnPUnV6AmNP3ecFawIFYdvJB_cm-GvpCSbr8G8y_Mllj8f4x9nBH8pQux89_6gUY618iYv7tuPWBFfEbLxtF2pZS6YC1aSfLQxeNe8djT9YjpvRZA

When we decode the JWT, it will look as

{
"alg": "RS256",
"typ": "JWT"
}
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022
}
signature{
}

When the grant-type is saml-bearer-grant, SP can exchange the saml assertion obtained from an Identity Provider which is trusted by the Identity Server. Similarly, In the JWT Bearer grant, SP has to exchange the JWT assertion obtained from a trusted IDP.

When a request is made to the token endpoint with the jwt-bearer-grant type, the JWT assertion, the client key, and client secret, the WSO2 Identity Server will read the grant type and trigger the JWT Bearer Grant Handler. This handler will check for the issuer of the JWT token and retrieve the IDP configuration. It will then obtain the public certificate of the IDP stored in the IDP configuration and validate the JWT. Once the JWT is validated, it will create an OAuth2.0 access token for the application holding the provided client key and client secret.

The Identity Server has two mechanisms to gets the public certificate of IDP in order to validate the signature in JWT assertion.

1- Using the public certificate uploaded via the management console( When configuring a trusted identity provider via the management console, public certificate can be uploaded in the management console).

2- Using the JWKS endpoint of the IDP.

Try out JWT-Bearer Grant with WSO2 Identity Server.

I’m going to configure WSO2 Identity Server as an Identity server in the primary Identity server.

1. How a JWT assertion should look like

JWT contains three parts that are separated by dots “.”: header, payload, and a signature. The header identifies which algorithm is used to generate the signature.

The payload contains the claims mentioned below:

  • iss (issuer) - The JWT must contain an iss (issuer) claim that contains a unique identifier that identifies the identity provider that issued the JWT.
  • sub (subject) - The JWT must contain a sub (subject) claim that identifies the entity that the identity provider or the entity that issued the JWT vouches for.
  • aud (audience) - The JWT must contain an aud (audience) claim which containing a value that identifies the authorization server as an intended audience. This value should be registered as token endpoint alias in the Identity Provider.
  • exp (expiration time) - The JWT must contain an exp (expiration) claim that limits the time window during which the JWT can be used.
  • nbf (not before) - The JWT may contain a nbf (not before time) claim that forces a JWT to be used only after a specified time.
  • iat (issued at) - The JWT may contain an iat (issued at) claim that identifies the time at which the JWT was issued.
  • jti (json web token Id) - The JWT may contain jti (JWT ID) claim that provides a unique identifier for the token.
  • Other custom claims — JWT may contain claims other than the above mentioned ones. This is the extension point of the JWT specification.

For example, see the following code block.

So when a jwt assertion sent to the WSO2 Identity Server, the assertion should look like as below:

{
"sub":"admin",
"aud":[
"https://localhost:9443/oauth2/token"
],
"nbf":1507546100,
"iss":"jwtIDP",
"exp":1507606100,
"iat":1507546100,
"jti":"Token56756"
}

2. Export the trusted IDP’s public certificate

Go to <Trusted_IS_HOME>/repository/resources/security/ folder and run the following command

keytool -export -keystore wso2carbon.jks -alias wso2carbon -file idp.pem

This command will export the public certificate of the trusted IDP

3. Configuring the primary IS

  1. Log in to the management console
  2. First, we will configure the trusted IDP. click on Main > Identity > Identity Providers and Add. I’m going to use the WSO2 Identity server as a trusted IDP.

2. Add the Identity Provider unique name. I configure it as jwtIDP

3. You can use JWKS endpoint of the IDP to verify the signature of the assertion or you can upload the public certificate of the IDP. Here I’m going to try with uploading the public cert of the IDP. Click the option button as upload IDP certificate and Click on the browse button and upload the public certificate (idp.pem)which you already exported (in the subsection 2).

4. Click on Register button and register the IDP

5. Configure playground2 as a service provider in the primary IS. Click on Inbound Authentication Configuration> OAuth/OpenID Connect Configuration and configure the SP.

Configure the callback URL: http://localhost:8080/playground2/oauth2client

6. Get the client_id and the client_secret of playground2.

client_id: 6ailnbS2x_IiBugPVKNKG3UHLX0a
client_secret: j1lohMVy30s77W7Jl8soOgFxCQsa

4. Configure the Trusted IDP

  1. Go the <Trusted_IS_HOME>/repository/conf/ identity.xml file and change the <IDTokenIssuerID> as the unique identifier that identifies the identity provider (the unique name of the trusted IDP that is configured in the primary IS). This should be done before the very first startup. In my case, it is jwtIDP.
<IDTokenIssuerID>jwtIDP</IDTokenIssuerID>

2. Log in to the management console of the trusted IDP and configure playground3 as a service provider.

3. Create a new service provider and Go to Inbound Authentication Configuration.

4. Enter the callback URL and Enable Audience Restriction. Give the token endpoint of the primary IS as an audience value

Callback URL : http://localhost:8080/playground3/oauth2client
Audience: https://localhost:9443/oauth2/token

5. Using the playground3, obtain an ID token from the trusted IDP. You can simply use response type as as implicit and get a id_token via browser . Note the client_id of the playground3 app.

client_id: 0fpfXVx1IbSGv0qRetyyYRH_xNoa

6. Configure the client_id and Send the following request to the /authorize endpoint of the trusted IDP.

https://localhost:9444/oauth2/authorize?response_type=id_token token&client_id=<client_id>&nonce=oidc&redirect_uri=<callback_url>&scope=openid
https://localhost:9444/oauth2/authorize?response_type=id_token token&client_id=0fpfXVx1IbSGv0qRetyyYRH_xNoa&nonce=oidc&redirect_uri=http://localhost:8080/playground2/oauth2client&scope=openid

7. Obtain the id_token from the trusted IDP and You can use jwt.io to decode it.

The obtained id_token and the decoded are given below.

eyJ4NXQiOiJOVEF4Wm1NeE5ETXlaRGczTVRVMVpHTTBNekV6T0RKaFpXSTRORE5sWkRVMU9HRmtOakZpTVEiLCJraWQiOiJOVEF4Wm1NeE5ETXlaRGczTVRVMVpHTTBNekV6T0RKaFpXSTRORE5sWkRVMU9HRmtOakZpTVFfUlMyNTYiLCJhbGciOiJSUzI1NiJ9.eyJhdF9oYXNoIjoiTElTRDZlczl5cWpPNHlRelVJTE1TZyIsImF1ZCI6WyIwZnBmWFZ4MUliU0d2MHFSZXR5eVlSSF94Tm9hIiwiaHR0cHM6XC9cL2xvY2FsaG9zdDo5NDQzXC9vYXV0aDJcL3Rva2VuIl0sInN1YiI6ImFkbWluIiwiYXpwIjoiMGZwZlhWeDFJYlNHdjBxUmV0eXlZUkhfeE5vYSIsImFtciI6WyJCYXNpY0F1dGhlbnRpY2F0b3IiXSwiaXNzIjoiand0SURQIiwiZXhwIjoxNTY5MDgzNDkxLCJpYXQiOjE1NjkwNzk4OTEsIm5vbmNlIjoib2lkYyIsInNpZCI6IjczN2UyYzk3LTkwZTEtNDA2ZC1hMWY5LTdjNTk3M2I3Njk4OCJ9.CEO4EwbywdFINLzWHfqV9CHgW9wU0etVVd8yaK0Tm1NOLBpolzCdpY2_GXEnl_8JM_1YK2vFY8BNtHh-VqugRr6IzddEBB1XAgmdRRK4MMMOwklDG1_9mDdlr6DnAzKQ4Og9iuVz6jkwIvsSYwy-2Y-idF3Qsco3benqYQq4Ee59dXCu7k4uqThe5xTiiMZfGgZTnss6n1Adu59ML0uPrHx-Z73-R_AzhrOAabqQgks4R9XyXiuiTGI1e3AmH6yDvp4E_yfZlBcXZL8Li-FQfZjBHcoDbHDAk5tgkhS5Rw7LQELoQIPi2VmEfYOJkhxQHwbKP1W6YNpCzEnuJc9tnQ

Try JWT bearer grant in the primary IS. Try the following curl command.

curl -k -v — user <client_id>:<client_secret> -d “grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&scope=openid&assertion=<assertion>” https://localhost:9443/oauth2/token

The sample request:

curl -k -v — user 6ailnbS2x_IiBugPVKNKG3UHLX0a:j1lohMVy30s77W7Jl8soOgFxCQsa -d “grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&scope=openid&assertion=eyJ4NXQiOiJOVEF4Wm1NeE5ETXlaRGczTVRVMVpHTTBNekV6T0RKaFpXSTRORE5sWkRVMU9HRmtOakZpTVEiLCJraWQiOiJOVEF4Wm1NeE5ETXlaRGczTVRVMVpHTTBNekV6T0RKaFpXSTRORE5sWkRVMU9HRmtOakZpTVFfUlMyNTYiLCJhbGciOiJSUzI1NiJ9.eyJhdF9oYXNoIjoiTElTRDZlczl5cWpPNHlRelVJTE1TZyIsImF1ZCI6WyIwZnBmWFZ4MUliU0d2MHFSZXR5eVlSSF94Tm9hIiwiaHR0cHM6XC9cL2xvY2FsaG9zdDo5NDQzXC9vYXV0aDJcL3Rva2VuIl0sInN1YiI6ImFkbWluIiwiYXpwIjoiMGZwZlhWeDFJYlNHdjBxUmV0eXlZUkhfeE5vYSIsImFtciI6WyJCYXNpY0F1dGhlbnRpY2F0b3IiXSwiaXNzIjoiand0SURQIiwiZXhwIjoxNTY5MDgzNDkxLCJpYXQiOjE1NjkwNzk4OTEsIm5vbmNlIjoib2lkYyIsInNpZCI6IjczN2UyYzk3LTkwZTEtNDA2ZC1hMWY5LTdjNTk3M2I3Njk4OCJ9.CEO4EwbywdFINLzWHfqV9CHgW9wU0etVVd8yaK0Tm1NOLBpolzCdpY2_GXEnl_8JM_1YK2vFY8BNtHh-VqugRr6IzddEBB1XAgmdRRK4MMMOwklDG1_9mDdlr6DnAzKQ4Og9iuVz6jkwIvsSYwy-2Y-idF3Qsco3benqYQq4Ee59dXCu7k4uqThe5xTiiMZfGgZTnss6n1Adu59ML0uPrHx-Z73-R_AzhrOAabqQgks4R9XyXiuiTGI1e3AmH6yDvp4E_yfZlBcXZL8Li-FQfZjBHcoDbHDAk5tgkhS5Rw7LQELoQIPi2VmEfYOJkhxQHwbKP1W6YNpCzEnuJc9tnQ” https://localhost:9443/oauth2/token

A sample response:

{
“access_token”: “c0a56e6f-c98c-396a-9662–34dad6d28934”,
“refresh_token”: “125aad34-fe75–3db2–9157–176ecdcebbc0”,
“scope”: “openid”,
“id_token”: “eyJ4NXQiOiJOVEF4Wm1NeE5ETXlaRGczTVRVMVpHTTBNekV6T0RKaFpXSTRORE5sWkRVMU9HRmtOakZpTVEiLCJraWQiOiJOVEF4Wm1NeE5ETXlaRGczTVRVMVpHTTBNekV6T0RKaFpXSTRORE5sWkRVMU9HRmtOakZpTVFfUlMyNTYiLCJhbGciOiJSUzI1NiJ9.eyJhdF9oYXNoIjoiNkw3dmliU2xma3I2SlRKNE1DWXhTdyIsImF1ZCI6IjZhaWxuYlMyeF9JaUJ1Z1BWS05LRzNVSExYMGEiLCJzdWIiOiJhZG1pbiIsIm5iZiI6MTU2OTA3OTk3OSwiYXpwIjoiNmFpbG5iUzJ4X0lpQnVnUFZLTktHM1VITFgwYSIsImFtciI6WyJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6Z3JhbnQtdHlwZTpqd3QtYmVhcmVyIl0sImlzcyI6Imh0dHBzOlwvXC9sb2NhbGhvc3Q6OTQ0M1wvb2F1dGgyXC90b2tlbiIsImV4cCI6MTU2OTA4MzU3OSwiaWF0IjoxNTY5MDc5OTc5LCJub25jZSI6Im9pZGMiLCJzaWQiOiI3MzdlMmM5Ny05MGUxLTQwNmQtYTFmOS03YzU5NzNiNzY5ODgifQ.bg4fQKueLrYLgl-QheBVKtMUruinFAeB2MFOXBEOrR4nWXt56DKYYMhXXAAp0hfY8ds3UF8taSNjwQcGK_DOgbMPppCS5Ef6rldjMt2roWOtMG1fWTKyuwW89C9y27-AJAtXx1u5b7xxax84aKLNwCrXZBgOeQc97S6zOmF8z_NCaR5gQwfd1hjo3fpi8wcP_bY25aG-TGw3UMujUP4ORpQbqNQbMazXk7UGCFM9F7XLC_bzRabcUexMLgT2Iim6OkkUFZ1gc553_2KEmDEaMDsNW_iZEUaCBBHB_zYr5Z0IDClbxXC3dhsPIT5mKLqxmAQ5zLOKHUWNk113IUdzIw”,
“token_type”: “Bearer”,
“expires_in”: 3600
}

For further information, refer WSO2 Official documentation:

https://docs.wso2.com/display/ISCONNECTORS/Configuring+JWT+Grant+Type

--

--

Piraveena Paralogarajah
Piraveena Paralogarajah

Written by Piraveena Paralogarajah

Software Engineer @WSO2, CSE Undergraduate @ University of Moratuwa, Former Software Engineering Intern @ WSO2

Responses (1)