Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
CarlosRoggan
Product and Topic Expert
Product and Topic Expert
11,758

Quicklinks:
Simplified Flow
Detailed Flow
Reference
Refresh Token
Use Tool


This blog is part of a series of tutorials explaining the usage of SAP Cloud Platform Backend service in detail.

In the previous blog postings, I’ve already explained how to call a Backend service API from an external tool (REST client), and how the OAuth security mechanism works in detail.
In both blogs we were using the OAuth grant type called “Password Credentials”, because it is a bit simpler.
In the present blog we’re going to use the grant type “Authorization Code”, which is more secure.

Note:
In this scenario, the end user's password is never visible, it is not sent as parameter

Prerequisites


Create API in SAP Cloud Platform Backend service.
Create XSUAA instance in your subaccount

Background


Roughly speaking, during the OAuth flow, the Client (e.g. web application) connects to the Authorization Server in order to get an access token on behalf of a user.
The Authorization Server grants access, if the user agrees, and sends a valid token. The Client uses this token to call the protected API.
The Authorization Server grants access only if some validations are successful (e.g. password, roles).
If the Client uses the grant type “Password Credentials”, then the Authorization Server returns the requested token. As explained before.
If the Client uses the grant type “Authorization Code”, then the process is a bit different. As explained below.

OAuth 2.0 Flow Overview


Below diagram depicts the OAuth 2.0 flow in a scenario where the grant type Authorization Code is used.



 

- The user opens an app (usually a web application, in our case the REST client)
- The app sends a request to the Authorization Server which displays his particular login screen
- The Authorization Server sends an authorization code (back) to the client app
- The client app sends a request to the Authorization Server, including the authorization code
- The Authorization Server responds with an access token
- The client app uses the access token to call the desired API

Note:
In case of “Password Credentials”, the login popup is showed by the Client
This implies that the client app gets to know the user password, so it must be a trusted app.
In case of “Authorization Code”, the login popup is showed by the Authorization Server

If above steps sound confusing to you, consider dividing them as follows:

1. Fetch Code:
User opens client, logs in to Authorization Server, which sends code to client
2. Fetch Token :
Client uses code to obtain token from Authorization Server, on behalf of user
3. Call API
Client calls API using the token

This structure makes clear, what’s the difference and benefit, comparing to the “Password Credentials” grant type:
The client does never get to know anything about the user’s password. Instead it deals only with the Authorization Code which expires immediately.
Drawback: the client needs to be able to receive incoming request. See below.

I've described the OAuth flow 2 times, one is easier and faster, the other one more interesting.
Both do work and are almost the same.

Simplified API call using “Authorization Code” grant type


In this description we ignore the redirect-uri.

0. XSUAA


Create a new instance of XSUAA service as described here or reuse an existing instance.
Anyways, take a note of clientid and clientsecret.
For example:

"clientid":     sb-yourinstance!t12345
"clientsecret": PBJvwKn1OuL51zgisYGLZPYqxa0=


 

1. Fetch Authorization Code


First step in the OAuth flow is:
The end user opens the client application (in our case: the REST client) and is forwarded to the Authorization Server which displays a login screen, where he enters the credentials.

So for us, the first step is to call the Authorization Server to get the Authorization Code.
This request can be done in a browser window.
We compose the request as follows:

Compose URL

As already known, use the url property from service key of xsuaa instance.
Append the endpoint which is responsible for issuing an authorization code:
oauth/authorize

Note:
You may refer to the OAuth 2.0 spec

Add these parameters:
&response_type=code
&client_id=<yourclientidFromXSUAA>

URL template:
https://<subacc>.authentication.<...>/oauth/authorize?&response_type=code&client_id=<id>;

Example URL:
https://acc.authentication.eu10.hana.ondemand.com/oauth/authorize?&response_type=code&client_id=sb-myuaa!t12345

Call URL

Invoke the URL in a browser.
You get a login screen.
Note:
For Trial users:
Below screenshot shows that the "user" is an email, so we enter the mail of our Trial user (not the userid "P12345..." )



Note:
If the logon is not shown, then the browser has probably cached the credentials. If these credentials are ok, you can just continue, otherwise you should clear the browser cache

Below screenshot shows that the logon screen is sent by the XSUAA instance:



Enter the credentials and press the "Log On" button.
The result is an error message, but don’t panic: it is expected (details in below section)
Just ignore it and have a close look at the URL:



The Authorization Server has tried to send the Authorization Code to a default server address which doesn’t exist.
BUT the good news: the URL contains the Authorization Code
Take a note of the code , e.g. ABcNUi2t3O

2. Fetch Access Token


OK.
Now we have the code.
Now we need the token.

Fetching the token is similar like described in a previous blog

Compose URL

The endpoint for oauth/token as described in a previous blog plus the following parameters:
grant_type=authorization_code
&code=<yourCode>
&client_id=<yourClientId>

URL template:
https://<subacc>.authentication.<...>/oauth/token?grant_type=authorization_code&code=<cod>&client_id...;

URL example:
https://subacc.authentication.eu10.hana.ondemand.com/oauth/token?grant_type=authorization_code&code=RUh4otudtP&client_id=sb-myuaa!t12345

Note:
You see, the difference to grant type "Password Credentials" is that here we don't add credentials to the request. Instead, there's only the "code"

Call URL

In this example, we do the call in a REST client, which allows to enter clientid and clientsecret as username and password, choosing Basic Authentication.
Such request can be  stored and automated.
The request should look similar like this:



And the result should look similar like this:



Note:
The AllAccess scope must be listed, as marked in the screenshot, otherwise the token is not valid

Note:
This request can be done in a browser window. It will prompt for credentials, where the clientid and clientsecret have to be entered manually

Note:
Troubleshooting:
While copy and pasting, make sure that there aren’t any blanks in the URL. A convenient reason for errors
If you get an error response, make sure that you properly copied the clientid and clientsecret
Try requesting a new authorization code, which expires after unsuccessful connection

Copy the token (as shown above, without the inverted commas)

3. Call API


Open a new REST client tab
Enter the URL of the API, with modified prefix (see previous blog to learn how to modify the API URL)

Authentication:
- Choose Authentication type as “Bearer token”, if supported by your REST client
Enter the copied token
- If your REST client doesn’t support that authentication type, then it is still possible to compose the       authentication as header:

Header name: Authorization
Header value: bearer eyJhsdfger...etc


Note that the word "bearer" has to be typed before the token is copied
This is shown in the screenshot below:



Finally, press "Send" and get the expected payload from the OData service (your Backend service API)

 

Detailed API call using “Authorization Code” grant type


In this description we create a dummy local server in order to follow the redirect.

0.1 XSUAA


Create a new instance of XSUAA service with the following json parameters:
{
"xsappname": "forAuthCodeWithScope",
"foreign-scope-references": [
"$XSAPPNAME(application,4bf2d51c-1973-470e-a2bd-9053b761c69c,Backend-service).AllAccess"
],
"oauth2-configuration": {
"token-validity": 7200,
"redirect-uris": [
"http://localhost:3000"
]
}
}

Description:

“foreign-scope-references”:
This is the scope which is required by Backend service.
The user needs the “AllAccess” role, otherwise he isn’t allowed to call APIs
As a consequence, the token issued by XSUAA, needs to contain this foreign-scope-reference

“oauth2-configuration”:
This parameter isn’t mandatory

"redirect-uris":
Here we can enter a kind of white list of trusted valid uris or hosts which are allowed to receive the “Authorization Code”
For security reasons, it makes sense to specify the “redirect-uri” in the XSUAA configuration.
Like this, the Authorization Server will deny to follow malicious redirects.
Note: “The redirection endpoint URI MUST be an absolute URI”

"token-validity":
Here we can specify how long the access token should be valid, before it expires and needs to be fetched again.
The default is 43200 which corresponds to 12 hours
Note:
There's another parameter for "refresh-token-validity"

After creation of service instance, create a "Service Key" and take a note of id and secret

0.2 Local server for redirect


An important topic which we ignored in the simplified description above:

The end user is prompted for login by the Authorization Server.
After successful user login, the Authorization Server calls the redirect-uri, to send the Authorization Code.
That’s why it the "Authorization Code" scenario requires that the client is capable of receiving incoming requests (like mentioned above. If not possible, then a different grant type has to be used).
In our simplified description above, we just ignored that.
Ignoring was possible, because we can see the URL which is attempted to be invoked (see screenshot above)

Now, in this detailed description, we’ll quickly create a local server, allowing us receive the request.
Quickly creating a server is possible with node.js
If you’re new to node.js you can follow the description here.

Steps:

Install node.js
Install express
In your command prompt, navigate to a folder of your choice, e.g. tempserver
Execute the following command:
npm install express
Check your tempserver directory: a subfolder called "node-modules" has been created
Still in the same folder, create a text file with a name of your choice, e.g. "server.js"
Open the file with any text editor, e.g. Notepad
Enter the following content:
const express = require('express');
const app = express();

app.get('/myoauthclient', function (req, res) {
res.send(req.query.code);
});

app.listen(3000, function () {
console.log('=> Server running on port 3000');
})

Please forgive the minimalistic code.
What it does:
With the help of the “express” library, a server is started, it listens on port 3000
Furthermore, a REST endpoint is defined. Whenever this endpoint is called, the query parameter is extracted and returned in the response.
That’s all.

Save and close the file.
In the command prompt, still in the same folder (same folder like server.js and node-modules), run the following command to start the server:
node server.js
After one second you can see the output in the console: => Server running on port 3000

You can test your server by opening a browser and invoking the following URL:
http://localhost:3000/myoauthclient?code=123
Result:
The browser shows the number 123

Note:
To stop the server press the keyboard shortcut Ctrl+C in the command line. If it doesn't stop the process, press the shortcut 2 times

Note:
The above note was just a note, just in case you want to shutdown the server some day
But not now.
Now we need the server, so keep it running and ignore above note.

1. Fetch “Authorization Code”


This is the request to exchange credentials for Code
This step is the same like described above, just one little difference:
the URL

Compose URL

Compose the URL as described above and add the following parameters:
&response_type=code
&client_id=<yourClientid>
&redirect_uri=<yourClientUri>

Template URL:
https://<subacc>.authentication.<...>/oauth/authorize?&response_type=code&client_id=<id>&redirect_ur...;

 

Description of the parameters:

client_id:
This param is required, because the Authorization Server validates if the calling client is registered and valid
Note that no client_secret has to be sent

response_type:
Value is “code”.
This ensures that the “Authorization Server” reacts with sending the “Authorization Code”
Note:
This parameter identifies that the grant type “Authorization Code” is being used.
Because in case of grant type “Implicit”, we would enter “token”, to get the token immediately

redirect_url:
This param tells the Authorization Server to send the “Authorization Code” to this url.
In our tutorial we decided to enter only the host in the xsuaa, so we’re more flexible with possible endpoints, but still having enough security.
So we specify the full "redirect-url" in the parameter of this get-code-call

Note:
We aren’t sending the “scope” parameter, because we have specified it in the settings of XSUAA instance.
Since the Backend service requires the “AllAccess” role, we’ve entered it in the xsuaa

Example URL:
https://subacc.authentication.eu10.hana.ondemand.com/oauth/authorize?&response_type=code&client_id=s...

Call URL

Invoke the URL in a browser.
You get a login screen (like described above), enter your SAP Cloud Platform user credentials, e.g. the email of your Trial user
After successful login, the Authorization Server calls our redirect URL and adds the Authorization Code to the URL as query parameter
Our local node.js server receives the request and responds with sending the code and that is what we see in the browser: the “Authorization Code”

Below screenshot shows the response in the browser.

Example redirect URL:
http://localhost:3000/myoauthclient?code=7bQRR4Dn55

It is the redirect-URL of our local server and the query parameter with the Authorization Code.
Our local server implementation sends the Authorization Code as response to the browser, but that’s just convenience, it is not specified in the OAuth 2.0 spec



Note:
The Authorization Code lives shortly, it has to be requested again after it was used once

Note:
If you get an error message: “no client with requested id: …” then there might be a blank in the URL, due to copy&pasting the clientid
Or you might have to clear the browser cache, in case an old client is cached

Note:
Before doing the redirect, the Authorization Server validates the given parameter and values, and of course also the entered credentials of the cloud platform user

Note:
If you open the debugger tools of your browser you can see that the Authorization Server has done 2 requests:



Our original request to the XSUAA has been redirected to our local server, status code 302 “Found” is used like 307 “Temporary redirect” and the “Location” header displays the redirected URL, as desired by us:



 

Optional Test:
Invoke the code-fetch-URL with a different redirect server, e.g.

&redirect_uri=http://localhost:3001/myoauthclient

As a result, the Authorization server responds with an error message: “invalid redirect”

Now try to fetch the code and send an invalid scope.
For instance, append the following parameter to your get-code-URL

&scope=admin.changePWD

The result will be an error message telling that the scope is invalid.
This means that a malicious client won’t get a role which would allow to do any harm to the backend resources.
These are examples for the benefit of OAAuth 2.0 and “Authorizatino Code” grant type

2. Fetch “Access Token”


We call the token-endpoint of the Authorization Server.
This time we have to sent the redirect_uri parameter, although no redirect has to be done.
But it is required, because the Authorization Server checks if the uri is the same like in the previous request

Compose the URL

Template URL:
https://<subacc>.authentication.<...>/oauth/token?grant_type=authorization_code&code=<code>&client_i...;

Example URL:
https://subaccount.authentication.eu10.hana.ondemand.com/oauth/token?grant_type=authorization_code&c...

Open the URL in a new browser tab (you might need the first tab to re-fetch a valid code).
The credentials of the client are required, so the browser displays a popup



Note:
Of course, this request can be done in a REST client, so the client credentials can be entered as Basic Authentication

The Authorization Server validates if client and redirect match the stored settings, if the Authorization Code is valid and corresponds to the given client_id and of course if the client_id credentials are correct
As a result, the server sends the “access_token” in the response:



The response payload contains the following properties:

access_token:
This is what we need to copy, but without the inverted commas

token_type:
bearer: this value is needed as well, it has to be entered in the Authorization header when sending the request to the Resource Server

expires_in:
Here we can see that the value which we entered in the XSUAA settings has been considered

scope:
Same here: we can see if the role which we need to access the Backend service APIs has been assigned to the access token (remember, we specify this special required scope in the xsuaa)

refresh_token:
This token can be used to refresh the access_token. The difference is that the refresh token lives much longer.
Remember that the validity duration can be configured in the XSUAA settings

3. Call API


Use the access_token just like described before
Note that the Resource Server does validation of the access token. The token mustn’t be expired and must contain a valid user who has the valid necessary role(s).

Note:
Troubleshooting:
If you get an error message, try fetching a new access_token.
Check if the full token is copied&pasted into the API calling tool
Check if the token type ("bearer" or "Bearer") is contained in the value of the Authorization request header
Check the token-response if the required scope (AllAccess) is contained.
Check if the end user is a member of the subaccount in which the Backend service is deployed
Check if the end-user has the required role assigned via role collection (see description)
Check if the role collection is known to the Identity provider (see same description)

Summary


In this blog we've gone through the flow of an authorization scenario based on OAuth 2.0 using the grant type "Authorization Code".
This means that the Client needs to fetch a code before it can fetch a token. That token can then be used to call APIs created in SAP Cloud Platform Backend service

Links


See previous blog

Appendix 1: Quick Reference



  1. Get Authorization Code


https://<subacc>.authentication.eu10.hana.ondemand.com/oauth/authorize?&response_type=code&client_id...

  1. Get token


https://bssubaccount.authentication.eu10.hana.ondemand.com/oauth/token?grant_type=authorization_code...

  1. Call API


https://backend-service-api.cfapps.eu10.hana.ondemand.com/odatav4/DEFAULT/MYSERVICE;v=1/PRODUCTS

  1. Refresh token


https://<subacc>.authentication.eu10.hana.ondemand.com/oauth/token?grant_type=refresh_token&refresh_...;

 

Appendix 2: How to use the refresh token


In order to get an access token, the Client has to send a request including the Authorization Code.
However, since the Authorization Code expires immediately, the client has to send previously a request to get the code, which implies that the end user has to enter his credentials.
This 3-step-process can be simplified by using a refresh token.
This refresh token is sent along with the access token and duration of validity can be very long.
It can be used multiple times to fetch a new access token, once that is expired.
The refresh token doesn’t require a login from the end user. Only the client has to authenticate with client_id and client_secret.

Compose the URL

The refresh_token request is sent to the oauth/token endpoint of the corresponding XSUAA instance, like the access_token request

The parameters:
&grant_type=refresh_token
&refresh_token=<yourTokenFromPreviousRequest>
&client_id=<yourClientId>

Template URL:
https://<subacc>.authentication.<...>/oauth/token?grant_type=refresh_token&refresh_token=<token>&cli...;

Example URL:
https://subaccount.authentication.eu10.hana.ondemand.com/oauth/token?grant_type=refresh_token&refres...

HTTP verb:
GET
Authentication:
Basic Authentication using cliend_id and client_secret

The request can be executed either with a REST client, or with a browser which prompts for user/pwd

Appendix 3: Using REST client tool support


In a previous blog I described how to use Postman REST client tool support for OAuth 2.0 calls to Backend service API

The description was based on grant type “Password Credentials”
When using “Authorization Code”, some more details need to be entered.
It should be enough guidance to give a screenshot here, all the rest is the same
Use these template URLs to compose the endpoints

https://<acc>.authentication.eu10.hana.ondemand.com/oauth/authorize
https://<acc>.authentication.eu10.hana.ondemand.com/oauth/token

Note:
Postman displays the login screen of the XSUAA, where you need to enter the end user credentials, e.g. Trial user email



 
9 Comments