Skip to Content
Technical Articles

Consuming SAP on-premise data through SAP API Management

In my previous blog post, I showed how to set up a simple API and consume it from a node application (or rather from an Alexa Skill). While it certainly helped to illustrate the basics of working with API Management, it might also be a bit detached from real-life use cases. In practice you will most likely have some type of authentication and authorization check in place, to check incoming requests before granting access to potentially sensitive data. In this blog post is about working with authentication flows in SAP API Management.

For that purpose, I will be working with the ES5 SAP Gateway Demo System, to simulate an on-premise system that is connected to SAP Cloud Platform via the SAP Cloud Connector. Working with ES5 also provides the benefit of requiring login information to access data, which we can use as a simple example of working with authentication flows.

*%20The%20ES5%20Demo%20Gateway%20System%20is%20not%20really%20an%20on-premise%20system%2C%20but%20we%20will%20use%20it%20to%20mimic%20one

* The ES5 Demo Gateway System is not really an on-premise system, but we will use it to mimic one

Prerequisites

 

 

Setting up an on-premise system for testing

Additionally to those requirements, this time, an on-premise system is needed. I will be using the openly available ES5 system, for which you can create a user here. To test whether you have access to the system, open https://sapes5.sapdevcenter.com/sap/opu/odata/sap/SEPMRA_PO_APV/PurchaseOrders?$format=json and login with your username and password. If successful, you should be able to see information about some purchase orders.

Now that we have access to the system let’s connect it to Cloud Foundry. For this purpose, we will use the SAP Cloud Connector. Follow this guide on how to install and configure a Cloud connector instance on your local machine to connect to your Cloud Foundry Subaccount. Once completed, you can check your configuration by selecting the “Connectivity” item in the navigation menu of your CF Subaccount and navigating to “Cloud Connectors”. Under “Exposed Back-End Systems” you should be able to see the on-premise system with the virtual host you set up when configuring the Cloud Connector.

Setting up an API

Once you are done setting up the connection to your backend system, use the knowledge we acquired in my last post to set up an API Provider and an API in the API Portal.

For the API Provider, navigate to Configure, click on Create and set one up called “es5” with the following configuration:

Connection:

Property Value
Type On Premise
Host virtuales5
Port 8000
Location ID (leave empty)
Authentication None
Use SSL false
Additional Properties None

Catalog Service Settings:

Property Value
Path prefix /sap/opu/odata
Service Collection URL /iwfnd/catalogservice/ServiceCollection
Authentication type Basic
Username (your ES5 username)
Password (your ES5 password)

Now, use the API Provider you just created to set up an API with the following configuration:

Property Value
Select API Provider
API Provider (select the provider you just created
URL /sap/opu/odata
Host Alias (choose one of the options)
API Base Path /es5
Service Type ODATA

Save and deploy your API. To check whether everything is working correctly, open your API’s URL in your browser and authenticate with your ES5 credentials.

Making requests using Basic Authentication

Since each request we want to send to our ES5 API is authenticated by the ES5 system, we will need to send our ES5 credentials with every request. The easiest way of doing so is by adding a basic authentication header to the GET requests we are sending. Taking our previously built node application, we could extend it like so:

// Make axios get request to API url
const response = await axios.get(url, {
    // Add custom authorization headers
    auth: {
        username: "username",
        password: "password"
    }
});

This would, however, require us to store our login information in every application we want to send requests from, like our Alexa skill from previously. Which will inevitably introduce security concerns since we would have to store sensitive data on potentially vulnerable third-party systems.

To avoid this issue, we can let SAP API Management handle the authentication. For that, we will safe our login information in a key-value map on the API Portal and use policies to add a basic authentication header to incoming requests. This gives us the advantage of only having to store user information on SAP systems.

To create a key-value map, you will first need to access your API Portal, navigate to Configure and switch over to the “Key Value Maps” tab. Click on Create and enter the following:

Property Value
Name Es5BasicAuthCredentials
Encrypt Key Value Map true

Also, add the following two entries to the key-value map:

Key Value
username (your ES5 username)
password (your ES5 password)

Save your key-value map. Setting “Encrypt Key Value Map” to true will prevent anyone from reading the set values manually, and only API Management can access them.

For our purpose, we will want to add two policies:

  1. a policy that reads out values stored in our previously created key-value map
  2. a policy that creates and adds a basic authentication header to any incoming request

On the left side of your API portal, navigate to Develop and select the API you created. In the top right corner, click on Policies. Doing so will take you to the Policy Editor, where you will see an overview of the request flow.

If you want to learn more about how the flow works, I recommend checking out this blog post by Elijah Martinez.

Since we want to add two policies, first click on Edit. Next, select the PreFlow under TargetEndpoint and add (+) a “Key Value Map Operations” policy, which you can find under “Mediation Policies” on the right side of the Policy Editor.

Clicking on add (+) will prompt you with a dialogue field in which you name your policy.

Set “getEs5Credentials” as the policy name and make sure to keep Stream set to Incoming Request. Replace the preconfigured XML with:

<KeyValueMapOperations mapIdentifier="Es5BasicAuthCredentials" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
    <!-- Read parameter with key "username" and assign its value to private variable BasicAuthUsername-->
  <Get assignTo="private.BasicAuthUsername" index='1'>
    <Key><Parameter>username</Parameter></Key>
  </Get>
  <!-- Read parameter with key "password" and assign its value to private variable BasicAuthPassword-->
  <Get assignTo="private.BasicAuthPassword" index='1'>
    <Key><Parameter>password</Parameter></Key>
  </Get>
  <Scope>environment</Scope>
</KeyValueMapOperations>

This XML snippet will read the values of the key-value map we defined earlier and save them as local variables, such that we can access them later. Next, add a “Basic Authentication” policy (under “Security Policies”) and replace the XML with:

<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>
 	<!-- for Encode, User element can be used to dynamically populate the user value -->
	<User ref='private.BasicAuthUsername'></User>
 	<!-- for Encode, Password element can be used to dynamically populate the password value -->
	<Password ref='private.BasicAuthPassword'></Password>

 	<!-- Assign to is used to assign the encoded value of username and password to a variable. This should not be used if the operation is Decode -->
	<AssignTo createNew="true">request.header.Authorization</AssignTo>
</BasicAuthentication>

This XML snippet will use the two variables we assigned earlier to create a new authorization header for incoming requests,

Now, the PreFlow of your TargetEndpoint should look like this:

Notice the two policies we created and their order. First, read out the required values from the key-value map, then use those values to add a basic authentication header.

If we now make a call to our API, we should successfully get back the pruchase orders we already saw when directly accessing the ES5 system. You will notice, however, that we are now pretty much at the same point we were at the beginning of this blog post since now everyone can make calls to the API without authentication, which is not what we want. Here I want to introduce the concept of API Keys. I won’t go into full detail as to what an API key exactly is, but imagine it as a unique identifier, that can be used to authenticate you, or rather your program.

If you are curious to learn more about the concept of API keys, check out this article.

Using API Key Verification in API Management

Acquiring an API Key

With SAP API Management, two steps need to be completed to acquire an API key. First, we will need to publish the API we created in the previous step as a product. A product represents a set of one or more APIs that are published to developers who might want to work with them. If you recall, in my previous blog post, I differentiated between the API Portal and Developer Portal. By publishing an API as part of a product, we make it accessible in the Developer Portal and by doing so, to our developer colleagues.

To create a product, access your API Portal and navigate to Develop > Products and click on Create.

Under the Overview tab, enter the following properties:

Property Value
Name demo-product
Title Product containing ES5 API
Description Simple demo product publishing ES5 API

Next, switch over to the APIs tab and add the API you created for the ES5 system in the previous steps.

Publish your product.

Now, switch over to the Developer Portal. There you should see the product you just created.

Clicking on it will show you detailed information about the product, such as the APIs contained in it. In order to acquire an API key for your API, you will need to register an application with the product you published the API in. To do that, click on Subscribe > Create a new Application.

This will prompt you with a window in which you can set up your application. Enter “demo-app” as the name. The product containing your API should already be selected.

Once you created your application, SAP API Management will automatically assign an API key to you, which you can access on your application’s overview page. Here you can also regenerate your key in case it has been unintentionally leaked to the outside.

To add the API key to our request, we can simply add the key as a request header. This is easiest done by recycling the code snippet we used earlier to make a basic authentication request:

const axios = require('axios');
require('ssl-root-cas').create().addFile(__dirname + "/../certs/es5-cert.pem");

const url = "https://devrelations.test.apimanagement.eu20.hana.ondemand.com/es5";

(async function() {
    // Make request to API
    try {
        const response = await axios.get(url, {
            // Add custom ApiKey header
            headers: {
                "ApiKey":"erDcyxv5WsDuK1XJ4AaQ3hyLV1POSQhN"
            }
        });
        console.log(response.data.value);
    } catch(e) {
        console.error(e.toJSON().stack);
    }
})();

Notice the modification we made to our code: we are now adding a header containing the API key to our request.

Checking API Keys of incoming requests

To verify the API key passed with an incoming request, we will once again create a policy in the policy editor of the API Portal. Navigate to your API and access the policy editor. Click on Edit, select the PreFlow under TargetEndpoint and create a new “Verify API Key” policy. Enter verifyApiKey as the name of the policy and replace the pre-defined XML text with:

 <!--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.ApiKey'/>
</VerifyAPIKey>

Notice that we specify to take the API key from request.header.ApiKey, since that is how we configured our request earlier. Also make sure the policy you just created is the first one in the PreFlow, meaning in the flow diagram it should be on the left of the two policies we created prior. If that is not the case, you can use the left and right arrows at the top of the page to rearrange the policy order.

Update and save your API. Sending a request to the API without or with an invalid API key will now result in a 401 Unauthorized response, while using a valid API key will give us access to the data.

Revisiting the Alexa Skill

The purpose of using API keys is to have a secure way to consume SAP data from third-party platforms. The Alexa skill from my previous blog post is an example of software running on such a platform. To adapt it to our new API and API key requirements, replace the GetApiDataIntentHandler with:

const GetApiDataIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'GetApiDataIntent';
    },
    // Handle user's intent request
    async handle(handlerInput) {
        const baseURL = "https://devrelations.test.apimanagement.eu20.hana.ondemand.com/es5";
        const path = "/sap//SEPMRA_PO_APV/PurchaseOrders?$format=json";
        
        let speakOutput;
        try {
            // Get resources from API and format as readable string
            const response = await axios.get(baseURL + path, {
                headers: {
                    "ApiKey":"7iGuS5Yy28BVxfgR0yVC3fAvvyon4hLD"
                }
            });

            const usdOrders = response.data.d.results.filter(x => x.CurrencyCode === "USD");
            const totalAmount = (usdOrders.reduce((a, b) => a + parseFloat(b.GrossAmount), 0)).toFixed(2);
            speakOutput = `There are ${usdOrders.length} purchase orders with a total gross amount of $${totalAmount}.`;
        } catch(e) {
            // Error handling
            speakOutput = `An error occurred}`;
        }
        
        return handlerInput.responseBuilder
            .speak(speakOutput)
            .getResponse();
    }
};

Invoking our Alexa skill with “start demo” and requesting data from our GetApiDataIntent with “show me my data”, will now display the number and total gross amount of purchase orders provided by the ES5 system.

API insights and control

SAP API Management offers you several tools to view analytics of your APIs, on the API Portal as well as on the Dev Portal.

API Portal

The overview page of an API will display how many calls have been made to that API and how many of those calls resulted in a successful response. While this does not give you any detailed insights about how your API is performing, you can use it as an indicator of whether there is something wrong with your API policies. Additionally, you can start a debug session for your API via the context menu on your API’s overview page. Select the three dots in the top right corner and click on Debug. Once you start a debug session, you will be able to monitor incoming requests and their responses in greater detail.

If you want to make your API unavailable to the outside, you can also un-deploy your API – temporarily or permanently – from the context menu.

Dev Portal

The Developer Portal offers you insights into API calls made from your application. Using your application’s API key makes it possible to trace back a single request to one of your applications and is thusly used to display some application-specific analytics on your app’s overview page. Here you can also regenerate your API key in case it has been accidentally leaked. Keep in mind that you will need to update each instance where your API key is used.

Let’s recap

We have an on-premise system providing us with SAP data. We connected to that system using SAP API Management. Following the same principles as in my previous blog post, we created an API Provider and an API, to consume data from ES5. However, this time, we also added some policies to require API keys in incoming requests and adding basic authentication. This required some additional steps I did not cover in the previous blog post: publishing the API as a product and subscribing to it in the form of an application on the Developer Portal.

It should be noted however that the policy flow I created during this blog post is only supposed to be a demonstration, to show how policies can be used and what is generally possible with SAP API Management. An API key alone should not always be used as a sole mean of authentication; however, in this case, it doesn’t really matter since the ES5 system only offers basic authentication anyway. If you are dealing with sensitive data you should consider making use of advanced authentication flows, such as OAuth 2.0 and JWT tokens.

2 Comments
You must be Logged on to comment or reply to a post.
  • Hi Benno,

     

    Thanks for useful blog.Can we do session management using API management in the back end system? As in a way ending the session at the end of the call? The issue we are seeing is that   the http calls are going to a single server though  we are using load balancer url in cloud connector connection and this is not ending sessions at the end of it?

     

    Regards,
    Venkat