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
375

1. Background

Once an API has been exposed via the API Management, technically it can be consumed by any applications. Sometimes we have requirement to know who is the caller. One of the solutions is to ask the application put the client id into HTTP header as the X-App-Key, which can be used to determine the application name later. Another soluton is even more straightforward, that just let the applicaiton fill up 'Sender System Name' into the HTTP header as a property like 'SenderService'. However, both of the solutions have a disadvantage, that the application needs an extra step, which might be refused or forgetted.  

Actually, the OAuth Token generated by API Management does content the application information. The above steps with X-App-Key or 'SenderService' are a sort of redundant. Here is the analysis on how to get the application name from the OAuth Token, which is compulsory from the API consumers.

2. Analysis

Turn an API proxy into the debug mode and consume it. Check the policy 'VerifyOAuthToken'. We can see the variable 'developer.app.name' has been set by the framework, as below.

stephen_xue_0-1739246614012.png

Now let's consume BTP API by using the developer.app.name

stephen_xue_2-1739249046451.png

This is part of the response:

stephen_xue_4-1739249580542.png

As a comparison, This is how the application has been registered in the 'API Business Hub Enterprise'.

stephen_xue_6-1739249911451.png

Once the titile, short text or description information has got, it is not hard to tell the iflow or backend what is the sender system. 

From the high level design's perspective, we can come up with at least two kinds of solutions on it.

  • Solution 1. Set the 'developer.app.name' into HTTP header, and pass it to the CPI iflow.  Then get the application information in the CPI layer.
  • Solution 2. Get the application information in the API management layer by means of policy.

Solution 1 is easier to fulfill and easier to monitor, While solution 2 is more elegant and better modularization.

Solution 2 will be introduct in the below chapters.

3. Prerequisite

This is not a building from scratch tutorial. There are few prerequisites of the configuration which will not be introduced in details here. They have been listed here for the convenience of the new starters.

3.1 OAuth2 Token API

The OAuth2 token API is not mandatory option for API management authentication. whereas it is the chosen one for below tutorial. You can choose other types as well. Please refer to this blog post https://community.sap.com/t5/technology-blogs-by-members/oauth-2-0-authentication-for-api-management... for how to configure OAuth2 token API. 

3.2 Credential of CI

In order to check the effect of the process, an iflow will be wrapped up by the API proxy. Therefore the CI credential is needed to consume the iflow. Please refer here to get or generate service key for CI iflow: https://help.sap.com/docs/integration-suite/sap-integration-suite/creating-service-instance-and-serv... . make sure the plan is integration-flow

3.3 Create an iflow

The iflow is used to check the effect of the process. just try to make it as simple as possible. you may create your own. Here is mine and you can download it from https://github.com/stephenxue/GetAppName.git 

Make sure the HTTP Header property 'SenderService' is allowed to the iflow

stephen_xue_1-1739408386500.png

The logic of the iflow is just to read SenderService from HTTP header and put it into the response message as below

stephen_xue_2-1739408576979.png

4. API Manegment Configuration

4.1 Prepare for credential and URLs

Firstly, please get the CI credential and iflow URL.

Secondly, for consuming BTP Application API, we need a service key for service 'API Management, API portal' and plan 'apiportal-apiaccess'. 

stephen_xue_1-1739414478354.png

please store the service key. we will use it later. It should look like this

 

{
    "url": "https://ap10apiportal.cfapps.ap10.hana.ondemand.com",
    "clientId": "sb-apiaccess_1721265820743!b9999|api-portal-xsuaa!b160",
    "clientSecret": "73e9ea2d-5213-4e9e-8ccd-fb2ff4d6b0fc$tM3NaIK6owFXqWE3mcdmaokp5pwMfM9hTAOzItuMeslg=",
    "tokenUrl": "https://<client>-8x5whtls.authentication.ap10.hana.ondemand.com/oauth/token"
}

 

 

 

Thirdly, form the application API URL.

The format of the URL is:

 

https://<url of the above service key>/apiportal/api/1.0/Management.svc/Applications

 

please keep it.

4.2 Maintain Key Value Maps

Altogether 3 KVMs are to be created. 

4.2.1 For storing CI credential

It is better to be an encrpted KVM

stephen_xue_0-1739416216266.png

4.2.2 For storing Credential for generating API Token

it is better to be an encrypted KVM. fill up the clientId and clientSecret from above chapter

stephen_xue_1-1739416360613.png

4.2.3 For Storing URLs

Please create it as an un-encrypted KVM. Please fill up the Application API path into URL_API_Application and fill up tokenUrl into URL_API_Token as below

stephen_xue_2-1739416714961.png

4.3 API Manegement Policy Configuration

The process consists of 11 policies, with 9 of them in the Proxy Endpoint and 2 of them in the Target Endpoint

stephen_xue_0-1739417231073.png

stephen_xue_1-1739417289193.png

The whole policies are put into a policy template which can be downloaded from https://github.com/stephenxue/GetAppName.git 

This is the detail configuration information per policy.

4.3.1 Policy OAuthV2

This policy is used to verify OAuth2 token from the consumer request.

 

<OAuthV2 async="false" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
   <!-- By default, VerifyAccessToken expects the access token to be sent in an Authorization header. You can change that default using this element<AccessToken> -->
   <!-- If you want to pass access token in an customer header "access_token": -->
   <!-- <AccessToken>request.header.access_token</AccessToken> -->
   <!-- If you want to pass access token in query param "access_token": -->
   <!-- <AccessToken>request.queryparam.access_token</AccessToken> -->
   <!-- this flag has to be set when you want to work with third-party access tokens -->
   <ExternalAuthorization>false</ExternalAuthorization>
   <!-- valid values are GenerateAccessToken, GenerateAccessTokenImplicitGrant, GenerateAuthorizationCode ,
    RefreshAccessToken , VerifyAccessToken , InvalidateToken , ValidateToken  -->
   <Operation>VerifyAccessToken</Operation>
   <GenerateResponse enabled="true"/><SupportedGrantTypes/>
   <Tokens/>
</OAuthV2>

 

It sets the variable 'developer.app.name' automatically with the application ID.

4.3.2 Policy LookupCache

This is the step reading stored API Token from cache.

 

<!-- configures how cached values should be retrieved at runtime -->
<LookupCache async="false" continueOnError="false" enabled="true" xmlns='http://www.sap.com/apimgmt'>
	<!-- configures a unique pointer to a piece of data stored in the cache -->
	<CacheKey>
		<KeyFragment>APIM_Token</KeyFragment>
	</CacheKey>
	<Scope>Global</Scope>
	<!-- the variable to which the cache entry should be assigned after it is looked up -->
	<AssignTo>external_access_token</AssignTo>
</LookupCache>

 

  • if it is successful, it will assign the variable 'external_access_token' and set variable 'lookupcache.ReadTokenForAPIFromCache.cachehit' to true;
  • if it is not successful, it will set variable 'lookupcache.ReadTokenForAPIFromCache.cachehit' to false;
4.3.3 Policy KeyValueMapOperations The First

This step reads URLs which might be used later, including the Application API Path and the API token URL

 

<KeyValueMapOperations mapIdentifier="APIM_AppName" async="true" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
	<!-- PUT stores the key value pair mentioned inside the element -->
   <Get assignTo="URL_API_Token" index='1'>
      <Key>
         <Parameter>URL_API_Token</Parameter> 
      </Key>
   </Get>
   <Get assignTo="URL_API_Application" index='1'>
      <Key>
         <Parameter>URL_API_Application</Parameter> 
      </Key>
   </Get>
	<!-- the scope of the key value map. Valid values are environment, organization, apiproxy and policy -->
	<Scope>environment</Scope>
</KeyValueMapOperations>

 

4.3.4 Policies Renew the API Token

If the token retrieving step failed, it means the token has expired and needs to be renewed. All of the policies in this chapter should have a condition string below for a better performance.

 

lookupcache.ReadTokenForAPIFromCache.cachehit = false

 

4.3.4.1 Policy KeyValueMapOperations The Second

This step reads credential used for generating API Token.

 

<KeyValueMapOperations mapIdentifier="APIM_Credential" async="true" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
	<!-- PUT stores the key value pair mentioned inside the element -->
   <Get assignTo="private.client_id" index='1'>
      <Key>
         <Parameter>client_id</Parameter> 
      </Key>
   </Get>
   <Get assignTo="private.client_secret" index='1'>
      <Key>
         <Parameter>client_secret</Parameter> 
      </Key>
   </Get>
	<!-- the scope of the key value map. Valid values are environment, organization, apiproxy and policy -->
	<Scope>environment</Scope>
</KeyValueMapOperations>

 

4.3.4.2 Policy ServiceCallout The First

This step makes a call to the Token API to retriev the token for the BTP Application API

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ServiceCallout async="false" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
    <Request>
        <Set>
            <Headers>
                <Header name="Content-Type">application/x-www-form-urlencoded</Header>
            </Headers>
            <FormParams>
                <FormParam name="client_id">{private.client_id}</FormParam>
                <FormParam name="client_secret">{private.client_secret}</FormParam>
                <FormParam name="grant_type">client_credentials</FormParam>
            </FormParams>
            <Verb>POST</Verb>
        </Set>
    </Request>
    <Response>TokenResponse</Response>
    <Timeout>30000</Timeout>
    <HTTPTargetConnection>
        <URL>https://{URL_API_Token}</URL>
    </HTTPTargetConnection>
</ServiceCallout>

 

4.3.4.3 Policy ExtractVariables The First

This step derives response json message from the above step and retrieve the access token. At the same time, it derives token expiry time in second. 

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ExtractVariables async="true" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
    <JSONPayload>
        <Variable name="external_access_token" type="string">
            <JSONPath>$.access_token</JSONPath>
        </Variable>
        <Variable name="expires_in" type="integer">
            <JSONPath>$.expires_in</JSONPath>
        </Variable>        
    </JSONPayload>
    <Source>TokenResponse</Source>
</ExtractVariables>

 

4.3.4.4 Policy PopulateCache

This step updates the API token into cache so that it can be reuse in future.

 

<!-- configures how cached values should be written at runtime -->
<PopulateCache async="false" continueOnError="false" enabled="true" xmlns='http://www.sap.com/apimgmt'>
	<!-- configures a unique pointer to a piece of data stored in the cache -->
	<CacheKey>
		<KeyFragment>APIM_Token</KeyFragment>
	</CacheKey>
	<!-- specifies the cache where the data is to be stored -->
	<Scope>Global</Scope>
	<ExpirySettings>
		<!-- the number of seconds after which a cache entry should expire -->
		<TimeoutInSeconds ref="expires_in"/>
	</ExpirySettings>
	<!-- specifies the variable whose value should be written into cache -->
	<Source>external_access_token</Source>
</PopulateCache>

 

Until this stage, the token has been renewed and stored in the cache.

4.3.5 Policy ServiceCallout The Second

This step makes a call to BTP Application API and get the app name in the response. This is the main step of the process. 

 

<!-- this policy lets you call to an external service from your API flow -->
<ServiceCallout async="true" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
	<!-- The request that gets sent from the API proxy flow to the external service -->
	<Request>
	    <Set>
	        <Headers>
	            <Header name="Authorization">Bearer {external_access_token}</Header>
	        </Headers>
	    </Set>
	</Request>    
	<!-- the variable into which the response from the external service should be stored -->
	<Response>sapapim.tokenresponse</Response>
	<!-- The time in milliseconds that the Service Callout policy will wait for a response from the target before exiting. Default value is 120000 ms -->
	<Timeout>30000</Timeout>
	<HTTPTargetConnection>
		<!-- The URL to the service being called -->
		<URL>https://{URL_API_Application}('{developer.app.name}')?$select=title</URL>
      <!-- The SSL reference to be used to access the https url -->
	</HTTPTargetConnection>
</ServiceCallout>

 

4.3.6 Policy ExtractVariables The Second

This step derives response from above step and get the title field which is the application name. Set it to the HTTP header as the property SenderService

 

<!-- Extract content from the request or response messages, including headers, URI paths, JSON/XML payloads, form parameters, and query parameters -->
<ExtractVariables async="true" continueOnError="false" enabled="true" xmlns='http://www.sap.com/apimgmt'>
    <!-- the source variable which should be parsed -->
    <Source>sapapim.tokenresponse</Source>
    <!-- Specifies the XML-formatted message from which the value of the variable will be extracted -->
    <XMLPayload>
        <Namespaces>
            <Namespace prefix="a">http://www.w3.org/2005/Atom</Namespace>
            <Namespace prefix="m">http://schemas.microsoft.com/ado/2007/08/dataservices/metadata</Namespace>
            <Namespace prefix="d">http://schemas.microsoft.com/ado/2007/08/dataservices</Namespace>
        </Namespaces>
        <!-- Specifies variable to which the extracted value will be assigned -->
        <Variable name="request.header.SenderService" type="string">
            <!-- Specifies the XPath defined for the variable -->
            <XPath>/a:entry/a:content/m:properties/d:title</XPath>
        </Variable>
    </XMLPayload>
</ExtractVariables>

 

All 9 policies above are placed in the Proxy Endpoint flow. The two policies below are placed in the Target Endpoint flow. 

4.3.7 Policy KeyValueMapOperations The Third

This step reads credential for CI layer

 

<!-- Key/value pairs can be stored, retrieved, and deleted from named existing maps by configuring this policy by specifying PUT, GET, or DELETE operations -->
<!-- mapIdentifier refers to the name of the key value map -->
<!-- Don't use Key Value Maps to store your logs as this can impact API Proxy runtime flow -->
<KeyValueMapOperations mapIdentifier="SCI_iFlow" async="true" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
	<!-- PUT stores the key value pair mentioned inside the element -->
   <Get assignTo="private.username" index='1'>
      <Key>
         <Parameter>username</Parameter> 
      </Key>
   </Get>
   <Get assignTo="private.password" index='1'>
      <Key>
         <Parameter>password</Parameter> 
      </Key>
   </Get>
	<!-- the scope of the key value map. Valid values are environment, organization, apiproxy and policy -->
	<Scope>environment</Scope>
</KeyValueMapOperations>

 

4.3.8 Policy BasicAuthentication

This step sets the base64 string of the credential for consuming the iFlow

 

<BasicAuthentication async='true' continueOnError='false' enabled='true' xmlns='http://www.sap.com/apimgmt'>
 	<!-- Operation can be Encode or Decode -->
   <Operation>Encode</Operation>
   <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
   <User ref="private.username" />
   <Password ref="private.password" />
   <AssignTo createNew="true">request.header.Authorization</AssignTo>
</BasicAuthentication>

 

Now all policies have been configured. Let's conduct the test for the result. 

5. Unit Test

Create an API proxy for the CI iFlow. Add the above policies to the proxy. Then add the API proxy together with a Token API proxy into a product, say S4_ERP. 

stephen_xue_0-1739443723904.png

5.1 Test for App01

Register an application for the product called App01. Store the App Secret and App Key

stephen_xue_0-1739443892022.png

Generate an OAuth2 token by using the above credential.

stephen_xue_1-1739444042001.png

Now consuming the API Proxy by using the above access_token

stephen_xue_2-1739444127472.png

We can see that the Application Name App01 has been determined.

5.2 Test for App02

Register an application for the product called App02. Store the App Secret and App Key

stephen_xue_3-1739444320547.png

Generate an OAuth2 token by using the above credential.

stephen_xue_4-1739444440089.png

now consume the API proxy by using the above access_token

stephen_xue_5-1739444512338.png

The Application Name has been determined. If the debug mode has been switched on, we can see that the token renew flow has been bypassed, since the below variable is true.

stephen_xue_6-1739444579946.png

6. Conclusion

By using the Application ID after the OAuthVerify policy and By consuming the BTP Application API, we can retireve the application name from the OAuth2 token. It is an option for you to implement the logic by means of policy configuration in the SAP API Management. This is the download link for all of the contents configured above. https://github.com/stephenxue/GetAppName.git please take your own risk to use it. 

 

Reference

For Policy Configuration https://community.sap.com/t5/technology-blogs-by-members/sap-api-management-policies/ba-p/13529965

Labels in this area