
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.
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.
Now let's consume BTP API by using the developer.app.name
This is part of the response:
As a comparison, This is how the application has been registered in the 'API Business Hub Enterprise'.
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 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.
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.
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.
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
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
The logic of the iflow is just to read SenderService from HTTP header and put it into the response message as below
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'.
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.
Altogether 3 KVMs are to be created.
It is better to be an encrpted KVM
it is better to be an encrypted KVM. fill up the clientId and clientSecret from above chapter
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
The process consists of 11 policies, with 9 of them in the Proxy Endpoint and 2 of them in the Target Endpoint
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.
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.
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>
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>
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
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>
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>
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>
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.
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>
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.
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>
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.
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.
Register an application for the product called App01. Store the App Secret and App Key
Generate an OAuth2 token by using the above credential.
Now consuming the API Proxy by using the above access_token
We can see that the Application Name App01 has been determined.
Register an application for the product called App02. Store the App Secret and App Key
Generate an OAuth2 token by using the above credential.
now consume the API proxy by using the above access_token
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.
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.
For Policy Configuration https://community.sap.com/t5/technology-blogs-by-members/sap-api-management-policies/ba-p/13529965
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
11 | |
10 | |
9 | |
7 | |
6 | |
5 | |
5 | |
5 | |
5 | |
5 |