Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
stephen_xue
Active Participant

Introduction


Different from generating an OAuth2 token in SAP API Management, there are quite a few ways to generate JWT token in the platform.

From the encryption type perspective, there are two ways:

  • HS256, synchronous algorithm

  • RS256, Asynchronous algorithm


For generating a token, RS256 needs a key-pair while HS256 needs a static string. Therefore, for RS256, the implementation method is a bit straightforward, whereas for HS256, there are a few options for choosing the 'static string'. It could be:

  • a static string in the Key Value Map;

  • the application key;

  • the application secret;

  • etc.


In the blog below, I will introduce one solution using HS256.

You can directly download policy template from here.

Token Generate Mechanism



Once subscribed an application in the developer portal, an application key and secret will be generated for the service consumer. The consumer should keep the key/secret in a safe place as a sort of credential.

For getting the JWT token, the service consumer provides the application key. In the API Management, after validating the application key, get the client_secret from variable and use the client_secret to generate the JWT token.

For validate the JWT token, the service consumer provides the client_secret and the JWT, of course. The API Management validates the JWT token by using the client_secret provided.

The advantage of the solution is, it gets use of application key/secret pair generated in separated calls(generate/validate) , which is safer than a static string. Compared with RS256,  it doesn't need a private/public key pair for the development which makes the solution easier.

 

Implementation Steps


Generate JWT Token


Create an echo ifow in CPI.


If you are in Neo, this step can be ignored. However I just found in Cloud Foundry, without an available target endpoint, the API proxy will always result in a HTTP 503 'Serviceunavailable' error. However this sympton doesn't exist in Neo.

The echo iflow can be very simple as below


This is the configuration of the HTTPS sender adapter. Remove the flag for csrf token.


write the URL down. it will be used later.



Configure API Proxy for generating token


Get into the API portal, create a new proxy

set the echo iflow URL has the target URL.


Alternatively, you can use  https://httpbin.org as the target. Then you do not need to fill up the authentication part. This is how it looks like


click create button.

add a resource for GET as below


 

This is an purely optional step. open the API Designer and make the modification to the YAML script as below


 

this is how the service looks like after the script has been added in the swagger hub style view



Add Policies



  1. Add a ValidAPIKey Policy


Add the policty to the 'Incoming Request' stream of Preflow as below


modify the script as below to derive value from HTTP header property: X-Api-Key



 <!--Specify in the APIKey element where to look for the variable containing the api key--> 
<VerifyAPIKey async='true' continueOnError='false' enabled='true' xmlns='http://www.sap.com/apimgmt'>
<APIKey ref='request.header.X-Api-Key'/>
</VerifyAPIKey>

 

2. Add an Assign Message policy to get key for JWT

Once the ValidAPIKey policy has been successfully processed, it will store the applicaiton key into variable client_secret. The Assign Message policy here derives the secret from the variable and populates it into the variable private.key for generating JWT token in the next step.



make sure the type is for request;

the script is to read value from varible client_secret and assign it to the variable private.key

Note: the variable format is verifyapikey.<the policy name>.client_secret

Since the policy making validation check in API key is CheckAPIKey, the name of variable is is

verifyapikey.CheckAPIKey.client_secret
<!-- This policy can be used to create or modify the standard HTTP request and response messages -->
<AssignMessage async="false" continueOnError="false" enabled="true" xmlns='http://www.sap.com/apimgmt'>
<!-- Sets a new value to the existing parameter -->
<AssignVariable>
<Name>private.key</Name>
<Ref>verifyapikey.CheckAPIKey.client_secret</Ref>
</AssignVariable>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<AssignTo createNew="false" type="request"/>
</AssignMessage>

 

3. Add a GenerateJWT Policy

This time the stream is Outgoing Response


now in the policy, the agorithm is HS256 as described in the beginning.  A token will be generated based on the value stored in the private.key.


The token will be stored in the variable jwt-variable


This is the script
<!-- Generate JWT TOken -->
<GenerateJWT async="false" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
<Algorithm>HS256</Algorithm>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<SecretKey>
<Value ref="private.key"/>
</SecretKey>
<ExpiresIn>1h</ExpiresIn>
<Subject>Sandbox JWT Token</Subject>
<Issuer>urn://Man in Black</Issuer>
<Audience>dummy</Audience>
<Id/>
<AdditionalClaims>
<Claim name="additional-claim-name" type="string">additional-claim-value-goes-here</Claim>
</AdditionalClaims>
<OutputVariable>jwt-variable</OutputVariable>
</GenerateJWT>

 

4. Add an Assign Message Policy to put the JWT Token in the response payload

Since in the last step, the generated token is stored in the variable jwt-variable. A policy is needed to duplicate the value into the response payload so that the service consumer can get it.

The stream is Outgoing Response



This is the script
<!-- This policy can be used to create or modify the standard HTTP request and response messages -->
<AssignMessage async="false" continueOnError="false" enabled="true" xmlns='http://www.sap.com/apimgmt'>
<!-- Sets a new value to the existing parameter -->
<Set>
<Payload contentType="application/json">{"token":"{jwt-variable}"}</Payload>
</Set>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<AssignTo createNew="false" type="response">response</AssignTo>
</AssignMessage>

 

The proxy should be like this now


Note:

if you use the echo iflow URL from CPI, another Basic Auth Policy needs to be added to the end of preflow.

Now, save the proxy and deploy it.

Test Get Token


Before conduct test, you need to publish a product based on the proxy created and subscribe an application to it , in order to get an application key / secret.

choose tab Product, then create


 

fill in a name and choose tab API


choose the API proxy just now deployed


Then publish it.


 

Now, go to developer portal. If you are in the cloud foundry, go to Business Hub Enterprise

choose the product published just now



subsribe an application to it


Fill in a name and the recall URL can be a dummy one


then the application key and secret will be created


record the application key


 

Go to resource tab of the API proxy


 

and you will get the token in the response after Execute button has been clicked.


by this means, we get a token generated from an application key of an applicaiton subsriber dynamiclly.

You can gain the token in your own app or POSTMAN as well. We will use POSTMAN to test the service later.

 

Validate JWT Token


To valid the token, create an another API proxy


 

Add an Assign Messge Policy to assign the private key


since the key is API Secret, we set the HTTP header X-Api-Secret to the variable private.key

Note: the VerifyJWT policy can only get key from private area.
<!-- This policy can be used to create or modify the standard HTTP request and response messages -->
<AssignMessage async="false" continueOnError="false" enabled="true" xmlns='http://www.sap.com/apimgmt'>
<!-- Sets a new value to the existing parameter -->
<AssignVariable>
<Name>private.key</Name>
<Ref>request.header.X-Api-Secret</Ref>
</AssignVariable>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<AssignTo createNew="false" type="request"/>
</AssignMessage>

then add the VerifyJWT Policy


This is the script.

Note: Please fill the value based on the GenerateJWT Policy configured.
<!-- Verify JWT TOken -->
<VerifyJWT async="false" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
<Algorithm>HS256</Algorithm>
<SecretKey>
<Value ref="private.key"/>
</SecretKey>
<Subject>Sandbox JWT Token</Subject>
<Issuer>urn://Man in Black</Issuer>
<Audience>dummy</Audience>
<AdditionalClaims>
<Claim name="additional-claim-name" type="string">additional-claim-value-goes-here</Claim>
</AdditionalClaims>
</VerifyJWT>

 

Add an Assign Message Policy to provide a response



<!-- This policy can be used to create or modify the standard HTTP request and response messages -->
<AssignMessage async="false" continueOnError="false" enabled="true" xmlns='http://www.sap.com/apimgmt'>
<!-- Sets a new value to the existing parameter -->
<Set>
<Payload contentType="application/json" variablePrefix="@" variableSuffix="#">{"name":"JWT Sandbox", "message":"successful"}</Payload>
</Set>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<AssignTo createNew="false" type="response">response</AssignTo>
</AssignMessage>

 

Unit Test


Positive Test


Get the token by using the application key


 

Fill the JWT Token to the bearer token field of the request in POSTMAN



 

Fill up the API Secret to the HTTP header with property name as below and you will get the mock response.



Negative Test Cases


Use invalid applicaiton key to fetch a JWT token


 

Expired JWT Token


 

Invalid JWT Token


 

Even with a correct JWT token, if the application secret is invalid


 

Conclusion


By using HR256, we can generate a JWT token based on the application secret, then we can get rid of private/public key pair and we do not need to use a static key.

 

GITHUB


Feel free to download the policy templates for both generate and validate JWT from here.

 

Suffix


UML Script Used for the flow chart
@startuml
title HS256 JWT Token Tutoria Process
autonumber
actor consumer
participant "Generate JWT Token" as proxy1 <<API Proxy1>>
participant "Validate JWT Token" as proxy2 <<API Proxy2>>


box "API Management" #LightGreen
participant proxy1
participant proxy2
endbox
participant "Echo URL or httpbin.org" as dummy

activate consumer
note over consumer: Get **application key**\nand **application secret**\nfrom developer portal

consumer -> proxy1: Retrieve JWT Token request\nwith **application key**
activate proxy1
note over proxy1: Validate the application key
note over proxy1: Get application secret\nfrom framework
note over proxy1: Generate JWT token by\nusing **applicaiton secrete**
proxy1 <-> dummy: Dummy call to the dummy service
proxy1 -> consumer: JWT token generated
deactivate proxy1
consumer -> proxy2: Request with JWT Token and **application secret**
activate proxy2
note over proxy2: Validate JWT Token by\nusing **application secret** provided\nby consumer
proxy2 -> consumer: dummy response
deactivate proxy2
deactivate consumer
@enduml

 
5 Comments
Labels in this area