Skip to Content
Technical Articles
Author's profile photo Carlos Roggan

SAP Cloud Platform Backend service: Tutorial [13]: API: called: from: external: tool

This blog is dedicated to Jan, because he asked for it
Hey Jan Penninkhof , hope you like it 😉

This blog is part of a series of tutorials explaining the usage of SAP Cloud Platform Backend service in detail.
Today: How to call APIs from external application like REST client

Quicklinks:
Configure Security
Obtain Token
Call API
Use Tool
Reference

Some things in life are so natural for us that we do it without even realizing it.
For example: breathing. Talk louder when we’re nervous. Use a REST client to call an API. Smile when we’re happy.
And so on.
Similarly, we take for granted that, after creating an API with Backend service, we go to our favorite REST client to use the API.
– Nope –
Doesn’t work:
<error>unauthorized</error>
An Authentication object was not found in the SecurityContext

brrrrrrrrr

But no need to be hopeless: there’s a series of tutorials….
Just follow this tutorial and you’ll be able to do what nature wants you to do.
And don’t worry: you don’t need to be a security expert for following this description.
And don’t worry: after following it, you won’t become a security expert.
If you’re interested, please read the OAuth blog as well

Background

At the current point in time, SAP Cloud Platform Backend service supports only a very limited set of authorization mechanisms:

  1. OAuth 2 authentication flow
  2. nothing else

Reason is the targeted usage by frontend applications rather than human testers.
However, there are reasons why we need to call the APIs also from local REST client: for example, because the cockpit is not powerful enough. This is the case whenever we need to send headers along with a request (e.g. for etag), or if we want to execute batch requests, or if we need to use PUT instead of PATCH
After going through this tutorial you’ll be able to easily test all of your APIs with a REST client.
You’ll learn that you cannot call your APIs simply providing your user credentials (Basic Authentication isn’t supported). Instead, you need to send an access token. This needs to be fetched once and can be used for some time.

These are the steps we need to do:

Overview of steps:

  1. Obtain authorization token

OK… I have to admit, this is a too rough overview.

Detailed overview of steps:

  1. Configure security in Cloud account
    1.1. Create xsuaa instance
    1.2. Create service key
  1. Obtain authorization token
  2. Call API

 

1. Configure security in Cloud account

We need a couple of preparation steps, to be done in our cloud account, before we can request an authorization token: For that purpose there’s the service offering “Authorization & Trust Management” in the SAP Cloud Platform.

Please follow this blog to create an service instance with specific parameters for Backend service in customer or trial account:
https://blogs.sap.com/2019/04/29/sap-cloud-platform-backend-service-tutorial-0.5-configure-xsuaa/

Before continuing with the next steps, make sure to view the service key which contains some information required below.
To view the service key:
Go to your BETA-enabled subaccount -> click your space -> Services -> Service Instances
Click your instance -> Service Keys -> select your service key

We take a note of 3 relevant property values (I’ve marked them in the screenshot above)

Note:
If you don’t see some properties, you might need to maximize your browser window, or use a different browser

In my example (slightly modified):

clientid sb-baas_access!t55555
clientsecret 2abcdefghijklmnopqrstuvwxy=
url https://subaccount.authentication.eu10.hana.ondemand.com

2. Obtain authorization token

Until now, everything was a one-time configuration in the cloud account.
Now we’re going to do the actual work:
Call the authorization endpoint to obtain that urgently desired token.
Without that token, we cannot call our API
Note:
This blog is all about that silly token, an endless chain of characters which don’t make any sense.
But they have a great value…

The URL

To obtain the token, we have to do a service call to the authentication server.
The url to be called is taken from the <url> – property mentioned in the previous section.
It is formed as follows:

https://<yourSubAccount>.authentication.<cloud>

The OAuth endpoint

At the end of above URL, we need to append the endpoint for oauth:

/oauth/token

Putting it together, in my example the endpoint URL looks like this:

https://subaccount.authentication.eu10.hana.ondemand.com/oauth/token

The grant type

Apart from the URL and the OAuth endpoint, there’s some more information required: the “grant type”.
We can pass it as query parameters along with the URL
These are the required parameters:

grant_type=password
   &username=<yourCloudUserEmail>
   &password=<yourCloudPwd>

So, putting all together, the final URL which we call to get the token:

https://...authentication.../oauth/token?grant_type=password&username=your@gmail&password=123

In my example (slightly obfuscated)

https://betasubaccount.authentication.eu10.hana.ondemand.com/oauth/token?grant_type=password&username=myuser@gmail.com&password=my123

Note:
Do I need to note this?
You know anyways very well that all this blog is for testing our APIs with REST client, to learn to build APIs with SAP Cloud Platform Backend service, to learn how to create and use some features (like etag, batch, etc) which cannot be tested with the cockpit.
As such, we can afford to send sensitive user credentials over the net.
In professional environment, you know that you would never send a password as URL parameter, as it would be easy to read it in HTTP logs.
BTW, using the tool support in Postman allows to at least hide the password in the UI. See appendix

 

Call the endpoint

To ask for the token, open a browser and pastenthat URL and press ENTER and…
– Nope –
You get a popup:

brrrrrrrrr
That endpoint requires authorization.
What now?
brrrrrrrrr
Yes, I know why you complain and yes, to get the authorization-token, we need another authorization.

Don’t panic, everything will be good.
We do have the necessary credentials: it is in the properties noted above
It is a simple Basic Authentication, where
“user” is the value of the property “clientid” and
“password” is the value of the property “clientsecret”

In my example, the (slightly obfuscated) credentials:

The response in the browser body contains a property “access_token” – and this exactly is what we need.
The value is a long long string with lots of beautiful characters and digits.


Don’t try to remember it or to type it.
Just copy and paste the value without quotation marks

That’s it: now we have the authorization-token.
Finally.
After a few notes, we can start using the token

Note:
You can repeat calling the OAuth endpoint – you’ll always get the same token.
Always?
No, not always. It loses its validity.
You can see it in the property “expires_in
In my example:
“expires_in”: 43199
It is measured in seconds.
In my example:
43199 would mean 720 minutes, or 12 hours, that’s 1 full day, means 2 working days
After expiration, repeat the request to the OAuth endpoint to get a fresh new token.

Note:
I assume your memory has an expiration date, like mine.
So, if you need to refresh your memory, don’t hesitate to revisit my blog
You can go directly to the appendix as a reference

Note:
To get the authorization-token, you can leverage your Postman REST client, it has built-in tool support which makes life easier.
See appendix

Note:
Remember: One of those unavoidable reactions is to smile when happy
I bet you’re smiling, after receiving this token – although it is ugly…
I can even hear you smiling…

Call API

Finally, we can call our lovely API (with REST client).
However, even this step needs a little explanation.
Actually we need 2 (two) explanations:
1. We need to find out the URL (yes, really)
2. We need to know how to specify the authorization

In detail:

1. The API URL

I bet you’re wondering, why we need any explanation at all about the URL of the API, as it is there in the cockpit of Backend service.
Yes, yes, the URL is of course taken from your API in Backend service….
However…
there’s really one thing you need to know:
The final URL It is different.
Slightly different.

Your API-URL is shown in the cockpit as follows:

https://subacc-backend-service.cfapps.xx.hana.on…d.com/odatav2/DEFAULT/SRV

However, for external call, the URL has different prefix:

https://backend-service-api.cfapps.xx.hana.on…d.com/odatav2/DEFAULT/SRV

So, instead of

. . .<yourSubAccount>-backend-service. . .

You have to use

. . .backend-service-api. . .

In my example, the final URL to be entered in REST client:

https://backend-service-api.cfapps.eu10.hana.ondemand.com/odatav2/DEFAULT/SRV/Products

2. How to set the authorization

In your REST client, go to “Authorization” section
Specify “Bearer Token” as authorization type
Copy the access token and paste it into the “Token” field

Note:
In case your REST client doesn’t support “Bearer Token” as authorization type:
You can enter the corresponding raw header.
The header entry would look like this (replace aaabbbccc … with your token):

Authorization: Bearer aaabbbccc…

In my example:

So, finally, after composing the URL and setting the authorization as described, you should get a success response after executing the request.

BTW:
All the procedure described in this blog can be used for any supported HTTP verb, like GET, POST, PATCH, DELETE

Note:
Once we have the token, we can call all different APIs of Backend service with it.
However, this statement holds only true as long as the API doesn’t require special role

That’s it, now we’re finally enabled to use our APIs in a more flexible way, calling them with a REST client tool.

Overview of the OAuth 2.0 flow:

Troubleshooting

What to do if you happily deploy everything and try testing and get….. Forbidden ???
See this blog for a Troubleshooting guide

Summary

In this tutorial we’ve learned how to call an API from an external application, e.g. REST client tool.
This is necessary, e.g. whenever we need to add custom headers (e.g. Etag)

There’s a prerequisite: create an instance of “xsuaa” service with service key.
3 properties of the service key are relevant for obtaining an access token
The procedure of externally calling an API requires 2 requests:
1. Call OAuth endpoint to get the access token in the response
2. Call API (with token and modified prefix)

Advertisements

Don’t miss the following appendix. It explains how to use the Postman REST client which has built-in tool support for OAuth 2.0 authorization

Once you have your scenario running, you might be interesting in learning a bit more about OAuth, please visit this blog

In this blog we’ve discussed the “Password Credentials” grant type. I’d like to recommend also to have a look at this blog which explains the “Authorization Code” grand type.

In this blog we’re using external REST client tool, manually executing service requests.
If you’d like to learn to do that programmatically with node.js, checkout this blog (and little series. Really interesting, you learn about cloud concepts as well, e.g. service binding and programmatically access cloud microservices)

Appendix 1: Using Postman’s OAuth 2.0 support

From all REST clients which I’ve tried, Postman is the only one which provides enough tool support to ease the OAuth authentication for our scenario.

Please follow below description, it makes your life easier.
Why?
You need to build only 1 request
You don’t need to deal with headers and params
The authorization details need to be entered only once
It is easy to refresh the token when expired

Proceed as follows:

1. In SAP Cloud Platform, Cloud Foundry Environment, go to your BETA enabled subaccount

2. To view the OAuth relevant information, do the following steps:
– Go to your space with the xsuaa service instance for Backend service
– Go to “Services” -> “Service Instances”
– Click your instance
– Click “Service Keys”
– Select your service key
– take a note of the values of the following properties.
In my example, with slightly obfuscated values:

url https://subaccount.authentication.eu10.hana.ondemand.com
clientid sb-baas_access!t55555
clientsecret 2tht5x7yCBSVnFdGp08lijlMojo=

3. To view your API URL, do the following steps
– Go to your Backend service cockpit and choose the API you wish to call
– copy the URL of the API (OData service)
– change the prefix of the URL
from:
https://yoursubaccount-backend-service.cfapps.xx.hana.ondemand.com/oda…
To
https://backend-service-api.cfapps.xx.hana.ondemand.com/oda…

4. To call your API with Postman, do the following steps:
– Open Postman
– Enter the URL of your API, modified as described above
– Click on the “Authorization” tab
– Click on the TYPE arrow
– Click on OAuth 2.0


– the right area is changed, such that it can deal with access token

– Since we don’t have an access token, we click the button “Get New Access Token”
– A popup is displayed “Get new access token”
Here we enter the required info, based on the properties which we’ve noted above:

Token Name any name of your choice, used to distinguish multiple tokens
Leave it empty if you need only one
Grant Type Password Credentials
Access Token URL append /oauth/token to the value of the <url>-property
e.g.
https://subacc.authentication.eu10.hana.ondemand.com/oauth/token
Username here you have to enter the user you use to login into SAP Cloud Platform.
In a trial account, it is your e-mail
Password the password of the user above
Client ID the value of the <clientid> property above,
e.g.
sb-baas_access!55555
Client Secret the value of the <clientsecret> property above,
e.g.
2skt7w1yCFJVnFdGp06lijlMojc=
Scope not required
Client Authentication Basic Auth header

Note:
If you try different REST client and grant type “Password” is not supported, then it doesn’t help to try e.g. “Client Credentials”, it doesn’t work, it is a different OAuth flow.

In my example (slightly obfuscated):


– Afterwards click on Request Token
– As a result, you get a popup containing the token (in a list along with other tokens, if you have requested more)

– No need to copy, just scroll down and press “Use Token” to use this token in your main Postman request

– The token is inserted into the “Token”-field and the Postman request is ready to be sent

That’s it, the request is successful.
No need to add more screenshots, so you have to believe it

Summary:
In Postman, create one request to call API (with modified prefix), choose “OAuth 2.0”-Authorization, use the tool-support to set the access token.

Appendix 2: Snippets

For your reference, please find below the relevant copy&paste snippets

Parameters for creation of xsuaa service instance

{
   "xsappname": "baas_access",
   "tenant-mode": "dedicated",
   "description": "Security profile of called application",
   "scopes": [
      {
         "name": "uaa.user",
         "description": "UAA"
      }
   ],
   "foreign-scope-references": [
      "$XSAPPNAME(application,4bf2d51c-1973-470e-a2bd-9053b761c69c,Backend-service).Admin",
      "$XSAPPNAME(application,4bf2d51c-1973-470e-a2bd-9053b761c69c,Backend-service).AllAccess"
   ],
   "role-templates": [
      {
         "name": "Token_Exchange",
         "description": "UAA",
         "scope-references": [
            "uaa.user"
            ]
      }
   ]
}

Parameters for the token-fetch

<Properties> and their values, to be copied from service key of xsuaa instance.
user credentials for grant type: SAP Cloud Platform user

Request URL:
<url>/oauth/token?grant_type=password&username=xx@yy.com&password=xxx

Authorization:
Choose “Basic Authorization”
Credentials:
user: value of <clientid> property
pwd:  value of <clientsecret> property

Header:
Content-Type: application/x-www-form-urlencoded

API-call
URL prefix: https://backend-service-api…..
Authorization: Bearer <yourtoken>

Appendix 3: obtain token with REST client

In above description we used the browser to fetch the authorization token.
It appears to be simpler: less cryptic, less settings, more user interaction

However, for those of you who are interested, let’s see the cryptic way of obtaining the token with REST client
It helps to complete the picture

Compose request

There are 2 possibilities:
1. Like above, use GET request and pass the parameters in the URL
2. Alternatively, use POST request and pass the parameters in the request body
Let’s use the second option here

In the REST client:

Enter the URL of the authorization server, as described above in this blog
<url>property with suffix /oauth/token

Choose POST as HTTP verb

In the “Authorization” Tab, choose “Basic Auth” and enter the credentials as described above in this blog:
Username is the clientid property
Password is the clientsecret property
Optionally, press “Preview Request”

Go to the “Headers” tab and enter the following header:

Content-Type:application/x-www-form-urlencoded

Note:
How to enter Basic Authentication in raw header section:
In case you need a raw authorization header, you have to use online base64-encoder to encode your password, then enter as follows:

Authorization: Basic c2ItYmFhEzMDIwOjJyaHQ1dzN6Q0JIVW5GZEdwMDhsaWpsVW9q . . . Yz0=

Below screenshot shows the raw headers:

Go to the “Body” tab and enter the OAuth grant type in the request body as described above in this blog.
Example:

grant_type=password&username=my@email.com&password=123

Press “Send” and receive status 200 and the access_token in the response body:

Copy the value of the token without the “
E.g.
eyJhbGciO . . . iJSUzI1NiIsImprdSI6Imh0dHBz . . .Oi8vYn. . . rZW5f

Then proceed calling your API as described above in this blog

Appendix 4: Hiding the password

If you’re concerned because in this explanation we’ve mentioned that the password is visible, as it is sent in plain text over the net:
Let’s see 3 possibilities to at least hide your password:

  1. Easy:
    Use the postman tool support, where the password is hidden. See appendix 1
  2. Less simple:
    Use refresh token. First obtain the token as usually, then use the refresh token, such that password is not required anymore. Explained in the next blog’s appendix
  3. More complex:
    Use grant type “Authorization Code“.
    In that case, the password is typed in the login screen of XSUAA, so the password is hidden and never sent in plain text

Assigned Tags

      18 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Enric Castella Gonzalez
      Enric Castella Gonzalez

      Great article! Thanks Carlos!

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Moltes gracies, Enric 😉

      Author's profile photo Jan Penninkhof
      Jan Penninkhof

      Hey Carlos! Thanks so much for following up on my request. Just took me a while to try this out. But this is where rainy public holidays are ideal for I guess!

      Worked like a breeze for me!

      As with the little included Postman class, I'd like to add that environments in combination with test can play a very nice role as well. Using a test you can e.g. pull the access_token from the first request, push it into the environment and then use it in the next request using e.g. {{access_token}}. Also works pretty nicely if you want to conceal passwords in a demo.

      Anyway, thanks so much again! This is a very helpful blog!

      Author's profile photo Souvik Majumder
      Souvik Majumder

      Hey Carlos,

      Nice article.

      I tried following all the steps. While I am getting the authentication token and the entire flow is running properly, I just cannot change the value of expires_in which is 43199 by default.

      How do I change that to some other value, let's say 5 minutes (300 seconds) ?

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      ​Hello  Souvik Majumder ,

      Thanks very much for your feedback!
      The value of expires_in can be controlled via configuration of the XSUAA instance:
      ​When you create the instance of XSUAA service, you pass a couple of parameters in JSON format, right? Here you can add one parameter for the oauth configuration, which in turn contains one parameter for the validity of the access token (and also for the refresh token):

         "oauth2-configuration": {
            "token-validity": 7200
         }

      I've described it a bit here:
      ​https://blogs.sap.com/2019/05/09/sap-cloud-platform-backend-service-tutorial-15-security-using-authorization-code-grant/
      and the reference of the parameters of XSUAA configuration can be found in the ​​SAP Help Portal​
      Hope this answers your question,
      Kind Regards,
      ​Carlos

      ​
      ​

      Author's profile photo Fouad Sebbane
      Fouad Sebbane

      Hey Carlos,

      This are indeed the tutorials which encourage me to give the SCP a shot. Excellent work!

      You described how to call an external API based on oDATA Service.
      https://backend-service-api.cfapps.xx.hana.on…d.com/odatav2/DEFAULT/SRV

      Do you have an example how to call it in case of nodejs in combination with the framework express?

      Many Thanks,
      Fouad

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Hello Fouad Sebbane ,

      Thank you so much for the nice feedback - which in turn encourages me to continue sharing info 😉
      Yes, I did the same next step, to call an oauth-protected service with node.js application.
      This example uses express:
      https://blogs.sap.com/2019/05/23/sap-cloud-platform-backend-service-tutorial-20-api-called-from-internal-node.js-with-binding/#code

      Please refer to this overview page, where you can find the different approaches to call such API from node:
      https://blogs.sap.com/2019/02/19/sap-cloud-platform-backend-service-overview-of-blogs/

      Hope it helps you!
      Cheers, Carlos 😉

      Author's profile photo Simon Roloff
      Simon Roloff

      Hi Carlos Roggan,

      thanks for the nice blog. Really helpful!!

      What is the reason, why the manual way only works when BETA features are enabled? (With postman I got it working without BETA features enabled.)

      Cheers,
      Simon

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Hi Simon Roloff Interesting point, I've no idea, I think there shouldn't be any difference.
      And thanks very much for the nice feedback 😉
      Cheers,Carlos

      Author's profile photo Mahesh Palavalli
      Mahesh Palavalli

      Hi Carlos Roggan ,

      Small question, You’ve asked us to change the url and make it a generic one(from the looks of it) which don’t have our subaccount name. What happens if two users have created odata with the same name, how will it get differentiated? (for one of my service I am using the same name ‘FACETSERVICE’ as yours)

      ** I am assuming it is because of the token which has the clientid, url and other info.. But let me know if it is something else.

      Thanks,
      Mahesh

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Hi Mahesh Kumar Palavalli ,
      Again a really good point which you - an experienced user - have pointed out ! 😉

      Backend Service is a multi-tenant-aware application. From my side, I'm not so multi-anything-aware, so I don't know the technical Details.  😉
      But I guess that somehow the Backend service stores metadata about every Service in separate schemas.  By no means it should be possible that a user accesses secret data just because the Service Name is equal.
      If you look into a JWT token, you'll see that it contains info like the identity Zone and issuer, etc, so the Backend Service can distinguish.

      Thanks and have a nice Weekend!
      Carlos

      Author's profile photo Mahesh Palavalli
      Mahesh Palavalli

      ? ?

      YThanks for the reply Carlos Roggan , Yeah  I’ve followed your another blog and checked it, the token has all the information. It’s first time for me to see such a non unique URL’s but still they are unique by using JWT.

      Enjoy the weekend!! 

      Author's profile photo Bin Xi
      Bin Xi

      Hi Carlos Roggan,

      Thanks for the blog.

      But I have a question.
      I want to call the backend service on behalf of nobody(No login user). So I choosed  [Client Credential] instead of [Password] to get the breaer token.I got the token but the scope is just [uaa.resource].

      I want to add some default scopes to the scope above to run my backend service.

      Otherwise I just got 403 Response Fobidden.

      So do you know how to solve this problem...

       

      Thanks

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Hello Bin Xi ,
      I haven't tried it, but it is a good question.
      Could you please check here, if this solves your problem?
      https://blogs.sap.com/2020/06/02/how-to-call-protected-app-from-external-app-as-external-user-with-scope/?update=updated#quickguide
      Section B would match your scenario, right?
      Please let us know if that works.
      Otherwise, we might have to wait for BS to add support for client-credentials-folw
      Thanks, Carlos

      Author's profile photo varun bhargav
      varun bhargav

      Hi Carlos Roggan ,

       

      I am unbale to generate the token for the custom IDP(SAP IAS) using the user name and password of a user in the same custom IDP.
      But, when tried with default it is working fine.
      I am trying it in SAP BTP

       

      Thanks in Advance.

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Hi varun bhargav ,

      thanks for the interesting comment - actually, I'm not familiar with custom IDP and IAS, but I think that there's quite a probability that password grant type is not supported.

      Kind regards,

      Carlos

      Author's profile photo Rauf Shaikh
      Rauf Shaikh

      Hi Carlos Roggan ,

      THanks for writing this detailed explanation.

      I am using SAP BTP Document Management System Integration Option, this requires to call CMIS API call to onboard a DMS repository i.e to connect to a repository, create folders etc.

      I am able to obtain JWT Authorization tokens using POSTMAN client, but now I want to make it dynamic and get JWT Auth Token programmatically in SAPUI5 built in SAP BAS.

      When I am making AJAX call to my URL https://...authentication.../oauth/token?grant_type=password&username=your@gmail&password=123 I am getting CORS issue, can you help in correcting below snippet?

      -Regards,

      Rauf

      Author's profile photo Carlos Roggan
      Carlos Roggan
      Blog Post Author

      Hi Rauf Shaikh ,

      This is rather a UI5-related question, right? I don't know what is the recommended way, how external calls should be done from ui5 code.
      - I assume, the typical option to avoid CORS, is to use destinations, isn't it?
      - Also, you could consider moving the logic to the service, which is used by your frontend. So it is easier to get the data from your ui5. Does that make sense?

      One more question: is it really necessary to use password_credentials to do your call? Usually, for service-to-service communication, client_credentials is sufficient. So you don't need to send user/pwd over the net. That could be read by anyone

      Kind Regards,

      Carlos