Technical Articles
SAP Cloud Platform Backend service: Tutorial [15]: Security: using “Authorization Code” Grant
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=<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_uri=<yourUri>
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=sb-client!t55555&redirect_uri=http://localhost:3000/myoauthclient
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_id=<id>&redirect_uri=<uri>
Example URL:
https://subaccount.authentication.eu10.hana.ondemand.com/oauth/token?grant_type=authorization_code&code=KUoOiTbnI4&client_id=sb-uaa!t12345&redirect_uri=http://localhost:3000/myoauthclient
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
- Get Authorization Code
https://<subacc>.authentication.eu10.hana.ondemand.com/oauth/authorize?&response_type=code&client_id=sb-myuaa!t12345
- Get token
https://bssubaccount.authentication.eu10.hana.ondemand.com/oauth/token?grant_type=authorization_code&code=hJ2ScIs&client_id=sb-myuaa!t12345
- Call API
https://backend-service-api.cfapps.eu10.hana.ondemand.com/odatav4/DEFAULT/MYSERVICE;v=1/PRODUCTS
- Refresh token
https://<subacc>.authentication.eu10.hana.ondemand.com/oauth/token?grant_type=refresh_token&refresh_token=<token>&client_id=<id>
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>&client_id=<id>
Example URL:
https://subaccount.authentication.eu10.hana.ondemand.com/oauth/token?grant_type=refresh_token&refresh_token=8ca9fb38c374413-r&client_id=sb-myuaa!t12345
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
Hi Carlos Roggan
For me it is not automatically redirecting to localhost:3000 it’s actually going to 8080 port. I’ve even deleted the service key and copy pasted ur code. One more thing It is also not going to the path :”myoauthclient” it’s just redirecting to ” http://localhost:8080/?code=VWkW36hOnK
It works with the redirect url directly mentioning in the auth code request though.
Note:

It stopped the server when I used CTRL + C for the first time and at the second time my system crashed. What did you make me do
Thanks,
Mahesh
Hi Mahesh Kumar Palavalli
Sorry sorry...I really hope that I didn't cause too much damage to your laptop... and smartphone and your car also not affected too much... 😀 😀
I remember I had similar observation, the redirect going to localhost:8080, which seems to be some default.
It makes sense to specify the redirect as parameter in the URL and use the xsuaa setting as whitelist
Cheers,
Carlos
😀 😀 I pressed the 3rd time and stopped all the destruction 😀
Ok got it, So the best and right practise is to pass the redirect url.
Thanks,
Mahesh
???good to know the fix: interrupt the destruction 😉
Hi Carlos,
thanks for that great article and detailled explanation.
I tried to setup the OAuth2 Authorization Code Flow like mentioned in your example but I am not able to fetch the Token without the Client Secret.
If I add the Client Secret to the Fetch Token Call it works, however I dont want to store this information on the clients. And as I understand thats the idea of the shortlived token which is sent back from the authorize endpoint.
Is there any setting in the XS-Security or OAuth Client which could lead to such an error?
Best regards,
Daniel
Please read my reply to you here:
https://answers.sap.com/comments/13392612/view.html
Thank you for the detailed explanation!
I have been trying to consume a backend service from a chatbot (Cognigy platform). The chatbot is hosted on a custom domain e.g. https://demochat-dev.company.cnb/.
When I add this URL as a redirect URI e.g redirect_uri=https://demochat-dev.company.cnb/authcall_back.html, I get an error "The redirect_uri has an invalid domain."
Can someone please help to understand the steps to add this as a valid domain on the SAP BTP subaccount?
Thank you!
Hi Carlos Roggan - Thanks for the blogpost. While we were deploying our multitenant App separately in a subaccount for eu10 region, we see that we face the issue of "The redirect_uri has an invalid domain." The API endpoint of the subaccount contains region = eu10-004 and hence the app url contains eu10-004 in it and the redirect_uri also contains it. But, while we see this in the url, under the environment variable VCAP_SERVICES for the XSUAA tag, we see that there is "eu10" in the authentication endpoint. Do you think having eu10-004 in the url would be the issue? If yes, how do we resolve this ? because we did not choose it to be eu10-004. I assume it is the current default for the EU region.
Thanks & Regards
Srinivas Rao.
Hi Carlos Roggan ! Thanks, very interesting post. But, I have problem. After used the refresh token I got new access token and apply that how usually, but I get mistake with my new access token.
I am try to find desition, but not result((( I need yor help!