Technical Articles
SAP Cloud Platform API Management – Self-Service Access Control
Lately a customer asked me if it was possible to check if an API consumer was actually reading only his data – within API Management. Although this is a typical use case for principal propagation (user ID is propagated to the backend, in which his rights are being enforced) I found the idea interesting.
More interesting is the fact that this use case demonstrates the flexibility of SAP API Management when it comes to customer-specific requests.
If you want to implement this use case, you’ll need to have access to an SAP Cloud Platform API Management environment and you should know the basics of API Management.
Detailed use-case description
As stated above, a company wants to expose data in a self-service mode to customers. This is done via an API that is managed centrally by API Management.
The company wants to enforce security on all levels, hence they also want API Management to check if the customer is accessing only his data.
Let’s imagine the API is an OData API. In order to retrieve data for the user (for instance a sales order), the application will call the resource like this:
/SalesOrderSet('0500000000')
To perform authentication, the application could send basic auth, an api key or a token to API Management. From these we could get the customer name.
But in order to simplify our example, we will use a query parameter containing the customer ID calling the resource:
customerIDParam = 0100000000
If the customer ID sent to the API proxy is matching the response payload (ie. if the customer requests his own sales order), everything is fine.
But let’s imagine someone reverse-engineers the App or Website and tries to access the sales order of other customers by altering the API call. This should be intercepted in API Management (and in the backend system using principal propagation).
Let’s see how to do this.
Create your API Provider and API Proxy
I will assume that you have a REST API backend in place. For testing purposes, you could use the SAP Dev ES5 system, an internet-available SAP dummy system. Here is how to get an account: https://developers.sap.com/tutorials/gateway-demo-signup.htmlÂ
In your API Management service, create the API Provider as follows:
Get the customer ID from parameter
As described before, we’ll need to get the customer ID first. This one is passed by the application as a query parameter. Obviously, for productive scenarios, you may want to use API key, OAuth or JWT tokens from which you can can get the user information.
For now, open the Policy Editor of your API proxy.
Select the “Sales Order Set” resource (by clicking on the blue “0” next to the name). This resource contains a sales order for a specific customer and can be filtered as needed.
Add an “Extract Variable” policy on the request:
<ExtractVariables async="true" continueOnError="false" enabled="true" xmlns='http://www.sap.com/apimgmt'>
<QueryParam name="customerIDParam">
<Pattern ignoreCase="true">{customerIDParam}</Pattern>
</QueryParam>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</ExtractVariables>
This policy will store the customer ID in a variable for later usage.
Get the customer ID from the response
Now we’ll get the customer ID from the sales order API call response.
We’ll do this using another extract variable.
Select the “Sales Order Set” resource.
Add an “Extract Variable” policy on the response:
<ExtractVariables async="false" continueOnError="false" enabled="true" xmlns='http://www.sap.com/apimgmt'>
<Source>response</Source>
<XMLPayload>
<Namespaces>
<Namespace prefix="d">http://schemas.microsoft.com/ado/2007/08/dataservices</Namespace>
</Namespaces>
<Variable name="customerIDPayload">
<XPath>//d:CustomerID</XPath>
</Variable>
</XMLPayload>
</ExtractVariables>
This policy will store the customer ID in a variable for later usage.
Compare actual customer ID and response customer ID
Because you cannot compare two variables directly in a proxy expression, we’ll use a small javascript that will do the trick.
Add a “JavaScript” policy on the response:
<Javascript async="false" continueOnError="false" enabled="true" timeLimit="200" xmlns='http://www.sap.com/apimgmt'>
<ResourceURL>jsc://compareCustomerIDs.js</ResourceURL>
</Javascript>
Add the actual Java Script in the “Scripts” section of the Policy Editor (left-hand menu) with the same name as above.
The code I used (I am no JS-god…) is as follows:
var customerIDParam = context.getVariable("customerIDParam");
var customerIDPayload = context.getVariable("customerIDPayload");
print("customerIDParam: " + customerIDParam);
print("customerIDPayload: " + customerIDPayload);
if (customerIDParam != customerIDPayload){
context.setVariable("differentCustomerID", "true");
}
else{
context.setVariable("differentCustomerID", "false");
}
As you can see, we are setting another variable “differentCustomerID” to hold the result of the comparison of the 2 customer IDs.
Add a conditional fault
Last step: let’s stop execution of the API proxy and raise a 403 error if different customer IDs are detected.
Add a “Raise Fault” policy at the end of the response lane:
<RaiseFault async="true" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
<FaultResponse>
<Set>
<Headers/>
<Payload contentType="text/plain"> </Payload> <StatusCode>403</StatusCode>
<ReasonPhrase>Unauthorized access</ReasonPhrase>
</Set>
</FaultResponse>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>
Now the trick is to raise this error only if the customer IDs are not the same, ie. if the variable created in the JavaScript is “false”.
Simply add the following condition to the “Raise Fault” policy execution:
(differentCustomerID == "true")
Your flow should look like this:
Testing time!
It is now time to test our little example.
I always recommend to use Postman to do API testing: you can thoroughly configure, save and organise your requests as needed.
Testing with the same customer ID in request and response
You will need to setup authentication for the backend system (eg. basic for SAP Dev ES5) in your API call.
Then use the following URL to call your API Management proxy:
https://<your APIM>.hana.ondemand.com:443/v1/AccessControlTest//SalesOrderSet(‘0500000000’)?customerIDParam=0100000000
The SalesOrderSet 0500000000 is the one of customer 0100000000.
No problem.
Testing with the different customer ID in request and response
Let’s see what happens if I try to request data from another sales order
The API proxy now throws an error: customer ID in the request param is not identical to the one in the response.
Wrap-up
As you can see, it is quite simple to add specific features in API Management, as request by your organization. Especially security is a very important aspect of API Management, but security should be implemented on every level. This includes verification of the access rights in the backend system/API, not only in the API Management layer.
Hi Sven Huberti ,
What is the type of authorization you have used in calling API end point?
I have developed APIs in API portal and created application in developer portal. Without giving my S-user ID, I'm not able to call the API end points. Is there any possibility of using API key and secret to call these end points? and in SAP documentation I see some mention of OAUTH URL but I couldn't find any template URL, I can use.