Thursday, 5 December 2019

Token API. JWT Grant.

Token API.
JWT Grant

1. Introduction
2. Configuring the JWT grant
3. Using the JWT grant
4. Interaction Diagram


1. Introduction

The JSON Web Token (JWT) bearer grant is simply a JSON string containing claim values that will be evaluated and validated by the JWT Grant Handlers at the Authorization Server end, before issuing an access token. WSO2 API Manager or WSO2 Identity Server, as an OAuth 2.0 Authorization Server with its Key Manager features, can accept JWT Assertions from OAuth 2.0 clients as means of resource owner authentication and authorization. Additionally, it can exchange the JWT token with OAuth 2.0 access tokens in order to access protected resources on behalf of the resource owner.








  •  Authorization grant types:
Kerberos OAuth2 Grant
Refresh Token Grant
Authorization Code Grant
NTLM Grant
Password Grant
SAML Extension Grant
Client Credentials Grant
Implicit Grant
JWT Grant

On this blog I am going to do a PoC for JWT Grant.

2. Configuring the JWT grant

This configuration can be done in the APIManager or in the Identoty Server. I am going to do it on the Identity Server because the API Manager, on my PoC, uses the Key Manager of the IS.

1) Log in to WSO2 IS.

2) Add a new Service Providers.
It is going to get JWT tokens on behalf of the users.













3) Fill in the Service Provider Name and provide a brief Description of the service provider.
Service Provider Name: jwt-tester


















4) Expand the OAuth/OpenID Connect Configuration and click Configure.

























5) The OAuth Client Key and OAuth Client Secret will now be visible.




















Take note of the OAuth Client key because it is going to be used by the IdP.

6) Go to Identity Providers section and click Add.
The image displays one configuration I have created in a previous blog.

















7) Provide the following values to configure the IDP:
Identity Provider Name: Enter an issuer name as the identity provider name. This is used to generate the JWT assertion. This value is the same that has been configured into
<wso2is-km-5.6.0>\repository\conf\identity\identity.xml file in the Identity Server.

/Server/OAuth/IDTokenIssuerID: apim-idp






Identity Provider Public Certificate: The certificate used to sign the JWT assertion. This is necessary to authenticate the response from the identity provider. On my PoC it is needed to import public certificated of the IS into the API Manager if the IS server is not running in localhost, because by default WSO2 includes certificates for localhost in all its products.

Alias: It is the OAuth Client key of the Service Provider.


















3. Using the JWT grant

1) Request for JWT token.
This request is going to be done using a password grant_type.
For instance:
curl -u ClientID_sp:ClientSecret_sp
     -k -d “grant_type=password&username=testuser&password=testuser”
     -H “Content-Type:application/x-www-form-urlencoded”           
     https://localhost:9444/oauth2/token

This is the same request/respose by SOAPUI to get a JWT token:
It uses ClientID_sp:ClientSecret_sp of the Service Provider.
















Response:
HTTP/1.1 200 OK
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-store
Date: Thu, 05 Dec 2019 10:26:31 GMT
Pragma: no-cache
Content-Type: application/json
Content-Length: 956
Server: WSO2 Carbon Server

{"access_token":"eyJ4NXQiOiJOVEF4Wm1N...qbCyWBdR_ZQ-hdk7tFA",
  "refresh_token":"08f71bbb-0c35-3e86-9e9e-6667ef5b2d09",
  "scope":"default","token_type":"Bearer","expires_in":3600}

2) Request for access_token.
And this is the request that uses JWT token to get an access token:
It uses ConsumerKey:ConsumerSecret of the Application in WSO2 Store.















Response:
HTTP/1.1 200 OK
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-store
Date: Thu, 05 Dec 2019 10:31:16 GMT
Pragma: no-cache
Content-Type: application/json
Content-Length: 168
Server: WSO2 Carbon Server

{"access_token":"1ad1208b-8785-337a-b397-969f3f2bfdc8",
 "refresh_token":"335a8b78-a9e8-371a-bc49-c84651485a40",
 "scope":"default","token_type":"Bearer","expires_in":3600}

Notes:
  • Tenant Domain
If the service provider and identity provider are in a tenant, you have to add the tenant domain as a query parameter to the access token endpoint. If the tenant domain is wso2.com, the access token endpoint will be as follows.

Access Token Endpoint: 
https://localhost:9443/oauth2/token?tenantDomain=wso2.com
  • JWT Bearer Grant
JWT contains three parts that are separated by dots ".":
header, payload, and a signature.

This is the JWT token decoded:























The payload contains the claims mentioned below:

Name Description
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.


4. Interaction Diagram

This sequence diagram displays the interaction between components to request for a JWT token and to use it.









Wednesday, 4 December 2019

Token API. Client Credentials Grant.

Token API.
Client Credentials Grant

1. Introduction
2. Client Credentials as an authorization grant.
3. Access Token Request
4. Access Token Response
5. Invoking the Token API to generate the tokens
6. Interaction Diagram


1. Introduction

APIs are group by Applications and protected by access tokens. If a Client needs to get access to an API firstly must to send a request to the Authorization server to get an access token. Authorization grant type groups different ways for ask for an access token. An authorization grant is a credential representing the resource owner's authorization (to access its protected resources) used by the client to obtain an access token.

Authorization grant types:

Kerberos OAuth2 Grant
Refresh Token Grant
Authorization Code Grant
NTLM Grant
Password Grant
SAML Extension Grant
Client Credentials Grant
Implicit Grant
JWT Grant

On this blog I am going to do a PoC for Client Credentials Grant
.

2. Client Credentials as an authorization grant.

On this use case the Client itself is the resource owner, that is because the access token request uses Client Credentials as an authorization grant. Authorization scope: Protected resources under the control of the Client. The Client can request an access token using only its client credentials (or other supported means of authentication) when the client is requesting access to the protected resources under its control, or those of another resource owner that have been previously arranged with the authorization server. The client credentials grant type MUST only be used by confidential clients.







Since the client authentication is used as the authorization grant, no additional authorization request is needed.

3. Access Token Request

The client makes a request to the token endpoint by adding the following parameters using the "application/x-www-form-urlencoded" format:
(Character encoding of UTF-8 in the HTTP request entity-body)

grant_type (REQUIRED): Value MUST be set to "client_credentials"
scope      (OPTIONAL): The scope of the access request.

For example:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic dG9tYXM6cmFiYXpv
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials

The authorization server MUST authenticate the client.

4. Access Token Response

If the access token request is valid and authorized, the authorization server issues an access token. A refresh token SHOULD NOT be included. If the request failed client authentication or is invalid, the authorization server returns an error response.

For example:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
    "access_token":"4598yutg4o3ph45ptu3p84up",
    "token_type":"example",
    "expires_in":3600,
    "example_parameter":"example_value"
}

5. Invoking the Token API to generate the tokens

1) Get a valid consumer key and consumer secret pair. Initially, you generate these keys through the API Store by clicking Generate Keys on the Production Keys tab of the application.


















2) Combine the consumer key and consumer secret keys in the format consumer-key:consumer-secret and encode the combined string using base64 (http://base64encode.org).

Consumer Key   : YzQHb9sUQeISS_KjZeuxRNPYhrka
Consumer Secret: k3EIaQrBMTtL9fFZN7OQ3fG15bca7
consumer-key:consumer-secret: YzQHb9sUQeISS_KjZeuxRNPYhrka:k3EIaQrBMTtL9fFZN7OQ3fG15bca
Base64         : WXpRSGI5c1VRZUlTU19LalpldXhSTlBZaHJrYTprM0VJYVFyQk1UdEw5ZkZaTjdPUTNmRzE1YmNh

3) Obtain the access token
Generic curl command:
curl -k -d "grant_type=client_credentials" -H "Authorization: Basic <Base64-encoded-client_key:client_secret>" -H "Content-Type: application/x-www-form-urlencoded" https://localhost:<https-port>/token -v

To obtain the access token by specifying the scope:
curl -k -d "grant_type=client_credentials&scope=test" -H "Authorization: Basic <ConsumerKey:ConsumerSecret>" -H "Content-Type: application/x-www-form-urlencoded" https://localhost:8243/token

To set a validity period for access tokens through a cURL command pass the validity_period parameter:
curl -X POST -k -H "Authorization: Basic <Base64(clientId:clientSecret)>" -d "grant_type=client_credentials&validity_period=<custom_validity_time_in_seconds>" -H "Content-Type: application/x-www-form-urlencoded" https://localhost:8243/token -v


Example:
curl -k -d "grant_type=client_credentials" -H "Authorization: Basic WXpRSGI5c1VRZUlTU19LalpldXhSTlBZaHJrYTprM0VJYVFyQk1UdEw5ZkZaTjdPUTNmRzE1YmNh" -H "Content-Type: application/x-www-form-urlencoded"

https://localhost:8243/token -v

Example with SOAPUI:














Response:
HTTP/1.1 200 OK
X-Frame-Options: DENY
Cache-Control: no-store
X-Content-Type-Options: nosniff
Pragma: no-cache
X-XSS-Protection: 1; mode=block
Content-Type: application/json
Date: Wed, 04 Dec 2019 13:52:37 GMT
Transfer-Encoding: chunked
Connection: Keep-Alive

{"access_token":"89d00c2f-48ef-35b2-a879-97cbe75a0b93",
 "scope":"am_application_scope default",
 "token_type":"Bearer",
 "expires_in":3600}

Request to APIGateway with the access token:




6. Interaction Diagram

On this diagram I summarize all the interactions between components.














APIM IS. APIs protected with self contained access token (JWT) using WSO2 API Manager and WSO2 Identity Server

APIM IS.
APIs protected with
self contained access token
(JWT) using
WSO2 API Manager and
WSO2 Identity Server

Index

1. Introduction
2. Configure IS to issue JWT self contained token
3. Configure service provider within the WSO2 Identity Server
4. Configure Identity Provider within WSO2 API Manager
5. Create backend mock service
6. Create an API on WSO2 API Publisher
7. Subscribe to the API on WSO2 API Store and tokens generation
8. Getting access to the API using JWT token
9. Summary of all interactions
10. Conclusion


1. Introduction

On this architecture one application needs to get access to a set of APIs, but to do so it needs an access token generated in another system. The token is self contained and because of that the API Manager does not need to validate the token connecting to the system who generated that token. For this PoC I have used SOAP UI as the Client and the Back end. This solution is the continuation of the blog: https://integration-wso2.blogspot.com/2019/11/apim-is-configuring-wso2-identity.html
It uses WSO2 Identity Server with the Key Manager and WSO2 APIManager.

This is what I am going to achieve:


2. Configure IS to issue JWT self contained token

File: <IS_HOME>/repository/conf/identity/identity.xml
Set the <Enabled> element (found under the <OAuth>,<AuthorizationContextTokenGeneration> elements) to true.
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: <UseMultiValueSeparator>false</UseMultiValueSeparator>







Add the following property under <OAUTH> section to use the JWT Token Builder instead of the default Token Builder:
<IdentityOAuthTokenGenerator>org.wso2.carbon.identity.oauth2.token.JWTTokenIssuer</IdentityOAuthTokenGenerator>









The “audiences” parameter can be configured so that the token includes information about the intended audiences who can use the generated token for authenticating the user:






<IDTokenIssuerID> name of the issuer of the token:





3. Configure service provider within the WSO2 Identity Server

This service provider is going to get JWT tokens on behalf of the users. Log in to WSO2IS as admin:admin, that is username and password.
https://localhost:9443/carbon
Now, go to Service Providers->Add:














Type name as: jwt-tester and Register.













Configure the service provider, push Edit button:












Select Inbound Authentication Configuration->OAuth OpenID Connect Configuration section and click on Configure button:



















Type the option as they are selected on the image. The callback URL has a random value. For this PoC I have typed: http://localhost:8080/playground2/oauth2























Take note of client key (OAuth Client Key) and client secret (OAuth Client Secret) of this service provider. That is shown under OAuth/OpenID Connect configuration section:



















4. Configure Identity Provider within WSO2 API Manager

Now, register into WSO2API Manager the IdP that issue the JWT tokens, that is: the Identity Server.
Login in https://localhost:9444/carbon
Go into Identity-> Identity Providers and click on Add:



















Identity Provider Name: apim-idp, This needs to be the same value as the <IDTokenIssuerID> value you configure at the identity.xml file of the IS. This value will be the issuer ID of the JWT token. Identity Provider Public Certificate, It is the public certificate of the WSO2 Identity Server in a pem file format. It is necessary to authenticate the response comingg from the identity provider (IdP). On this PoC WSO2 Identity Server is the IdP.

To create the public certificated from wso2carbon.jks file from IS:
<IS_HOME>/repository/resources/security/
type:
keytool -export -alias wso2carbon -file wso2.crt -keystore wso2carbon.jks -storepass wso2carbon






That wso2.crt public certificated can be imported with the button Choose File. Alias, this is the value of the clientID (client key) of the service provider configured in the WSO2 Identity Server. This will be checked when verifying the JWT token within the WSO2 API Manager.

Now, we can get access to the API with the JWT token issued by the IS.

5. Create backend mock service

The service that I am going to use is a GET call to this end point:
http://api.worldbank.org/countries/AW

As a response I get:
<data contentType="text/xml; charset=UTF-8" contentLength="611">
<![CDATA[?<?xml version="1.0" encoding="utf-8"?>
<wb:countries page="1" pages="1" per_page="50" total="1" xmlns:wb="http://www.worldbank.org">
  <wb:country id="ABW">
    <wb:iso2Code>AW</wb:iso2Code>
    <wb:name>Aruba</wb:name>
    <wb:region id="LCN">Latin America &amp; Caribbean </wb:region>
    <wb:adminregion id="" />
    <wb:incomeLevel id="HIC">High income</wb:incomeLevel>
    <wb:lendingType id="LNX">Not classified</wb:lendingType>
    <wb:capitalCity>Oranjestad</wb:capitalCity>
    <wb:longitude>-70.0167</wb:longitude>
    <wb:latitude>12.5167</wb:latitude>
  </wb:country>
</wb:countries>]]>
</data>

That endpoint can be tested using a browser or SOAPUI. This call is done with the browser:













And this one with SOAPUI:









To generate the Mock Service, select the url and right buttom and now select Generate REST Mock Service. I rename the mock as WorldBankWiremock:









This image shows the final result:






















I am going to use port 8089 for this mock:


















To test the mock service I have built another call to the mock: GET http://localhost:8089/countries/AW
Now, run the mock:

























and call the mock service:












Now, I am ready to create the api  in the API Manager.

6. Create an API on WSO2 API Publisher

I am going to login to the API Manager Publisher portal to create an API with the mock as a back end.

https://localhost:9444/publisher











Now, select ADD NEW API:













Select Desing a New REST API:














In the Design API page, type:
Name: WorldBank
Context: wb
Version: 1.0.0
URL Pattern: /countries/{code}
Select GET and Add:
























Type Next: Implement >, to go to the Implement page.
Select as an Endpoint Type: HTTP/REST Endpoint
And as an End Point: http://localhost:8089
Type: Next: Manage >

























Type Next: Manage > and select Transports: HTTPS, HTTP
Subscription Tiers: Gold
And Save & Publish

























This is the final result when the API has been created:





















7. Subscribe to the API on WSO2 API Store and tokens generation

After the API has been created now it is time to subscribe it through the API Store.
Try to get access to the Store with a new user, like: testuser.























This new user will be stored in the User Store (WSO2UM_DB) and this database is shared with the IS. This user and password will be used when requesting for a JWT to the IdP. To subscribe to the API, it is needed to do it through an Application.
Select the API:




















Now, select Default Application and Subscribe, as image shows:




















As a final step the keys to get access to the API must be generated, to do it, select the DefaultApplication, select Production Keys and Generate keys button:
























The Consumer Key, Consumer Secret and Test Access Token are generated:

























Take note of the client id and client secret. Those credentials are going to be used to get the access token with the JWT grant type.

8. Getting access to the API using JWT token

I am going to send a set of request with SOAPUI in order to get access to the API.
Do not forget to maintain Mock Server running.

Steps:
1) Get a JWT token from IS.
   Grant type is password, what means is that the request will use username and password in base 64 format as credentials. This request contains clientID and Client Secret in base 64 format of the Service Provider that was configured into IS.
 
   Service Provider:
ClientId     = gzV_0BRHXC4FuY7XqxjL3MMP7iEa
Client Secret= zkeiTP6JOX4nKiMmxKf0WGazK6Ia
 
ClientId:Client Secret = gzV_0BRHXC4FuY7XqxjL3MMP7iEa:zkeiTP6JOX4nKiMmxKf0WGazK6Ia
Base 64 format           = Z3pWXzBCUkhYQzRGdVk3WHF4akwzTU1QN2lFYTp6a2VpVFA2Sk9YNG5LaU1teEtmMFdHYXpLNklh (Used in the request for JWT token)

   Authorization: Base Z3pWXzBCUkhYQzRGdVk3WHF4akwzTU1QN2lFYTp6a2VpVFA2Sk9YNG5LaU1teEtmMFdHYXpLNklh












 
The image shows this request made in SOAPUI with the access_token as a response, that is the JWT token.















 
To decode JWT copy it and paste it here: https://jwt.io/

Response with the JWT token as access_token:
{"access_token":"eyJ4NXQiOiJOVEF4Wm1NeE5ETXlaRGczTVRVMVpHTTBNekV6T0RKaFpXSTRORE5sWkRVMU9HRmtOakZpTVEiLCJraWQiOiJOVEF4Wm1NeE5ETXlaRGczTVRVMVpHTTBNekV6T0RKaFpXSTRORE5sWkRVMU9HRmtOakZpTVEiLCJhbGciOiJSUz I1NiJ9.eyJzdWIiOiJ0ZXN0dXNlckBjYXJib24uc3VwZXIiLCJhdWQiOiJnelZfMEJSSFhDNEZ1WTdYcXhqTDNNTVA3aUVhIiwiYXpwIjoiZ3pWXzBCUkhYQzRGdVk3WHF4akwzTU1QN2lFYSIsInNjb3BlIjoiZGVmYXVsdCIsImlzcyI6ImFwaW0taWRwIiwiZXhw IjoxNTc1MzczODM4LCJpYXQiOjE1NzUzNzAyMzgsImp0aSI6ImEzYzY0OThmLWRiNGUtNGJhMy05MzA4LTYxYWI3YTQ4MDJhNiJ9.UnoUe-S3CymxXiz2gbuPznU9XisJ8k1bGQeMU36L_YZ8YX8s6NI571f1iKvC9hREzF0vGZH30EXDktfM_4ja3VVPomSSJnWU1eZ9EIifUtucxBrD96Ya5a9gqbAipfhMzA0Qm9NDvdPjrdeUmbN0QzNI7bWNOHvVJnitganLlBuecN89p0nWef3wPOTORMx1HpKW5EFGbb9h_LD91OgNUg6KwJnb6mQGxDvdSLYIzDXmiv8XMq7aCHX6DRlKfpDFv5wSawcZft1O6heOkagk3MJ_iRxMDY9kDoHYQxyoeg9MxS-V7ofIigql2zfQAAMpi2zJtNCzeJ7N2hA4jQFF4w","refresh_token":"abf2efd0-7c7b-3f2a-9326-9865d3893d03","scope":"default","token_type":"Bearer","expires_in":3600}

























As it can be seen, JWT has the ClientId of the SP that was configured into the IS. This value has been stored in the API Manager too to be compared or validated against the one store in the JWT.


2) Call the WSO2 API Manager with the JWT grant type, from the previous response, to get an access token. For this request use clientID and clientSecret in base 64 format of the Application used to subscribe to the API.
   grant_type: urn:ietf:params:oauth:grant-type:jwt-bearer
   assertion: <JWT from previous request>
   Consumer Key   = tIIquZ2gUwywsT5mnU56ANqpftoa
   Consumer Secret= dp5PqY6gLfQMEP1UOS1MRmNeX5Qa

   Consumer Key:Consumer Secret = tIIquZ2gUwywsT5mnU56ANqpftoa:dp5PqY6gLfQMEP1UOS1MRmNeX5Qa
   Base 64 format               = dElJcXVaMmdVd3l3c1Q1bW5VNTZBTnFwZnRvYTpkcDVQcVk2Z0xmUU1FUDFVT1MxTVJtTmVYNVFh

   Authorization: Base dElJcXVaMmdVd3l3c1Q1bW5VNTZBTnFwZnRvYTpkcDVQcVk2Z0xmUU1FUDFVT1MxTVJtTmVYNVFh

   The image shows this request made in SOAPUI with the access_token as a response, to get access to the API.















Response with the access_token:
{"access_token":"3c874bef-9430-3dd7-84bb-a2c95b684868",
 "refresh_token":"4e3cead2-31a4-3cfb-9544-c608bf3c3e8a",
 "scope":"default",
 "token_type":"Bearer",
 "expires_in":3600}

3) Use the access token to consume the API:














 
This is the response of the API Gateway:
<data contentType="text/xml; charset=UTF-8" contentLength="611"><![CDATA[?<?xml version="1.0" encoding="utf-8"?>
<wb:countries page="1" pages="1" per_page="50" total="1" xmlns:wb="http://www.worldbank.org">
  <wb:country id="ABW">
    <wb:iso2Code>AW</wb:iso2Code>
    <wb:name>Aruba</wb:name>
    <wb:region id="LCN">Latin America &amp; Caribbean </wb:region>
    <wb:adminregion id="" />
    <wb:incomeLevel id="HIC">High income</wb:incomeLevel>
    <wb:lendingType id="LNX">Not classified</wb:lendingType>
    <wb:capitalCity>Oranjestad</wb:capitalCity>
    <wb:longitude>-70.0167</wb:longitude>
    <wb:latitude>12.5167</wb:latitude>
  </wb:country>
</wb:countries>]]></data>

9. Summary of all interactions

As a brief description, I have written this interaction diagram to understand better the requests/responses between main components.











10. Conclusion

On this PoC I have used the Key Manager of the Identity server, but can be replicated with other 3rd party Key Manager. The same exercise can be done with WSO2 Micro Gateways where this component is able by itself, without connecting to external components like the Key Manager, to validate the token.
Download config files