How to get an access token from the XSUAA Service for external API accesses using the Password Grant with Client and User Credentials method
Introduction
Recently in the community a question was asked how an access token can be determined to access a XSUAA secured SAP HANA XS Advanced service (e.g. written in Node.js) directly from an external application. I think I gave a very detailed answer to that question, but I wanna write it down here also in a blog post, because there are some steps necessary to be able obtain an access token via the XSUAA API. As said in the title, the “Password Grant with Client and User Credentials” method is used and described in the following lines. Details about the API interface can be found in the official Cloudfoundry UAA documentation.
In summary following steps are necessary:
- Determine client credentials for XS Advanced service application.
- Determine the XSUAA server URL.
- Obtain an access token from the /oauth/token endpoint of the XSUAA server.
For test reasons I created a simple MTA application containing an Node.js module with a test endpoint returning a simple JSON object containing a “success” property to indicate that the call was successful.
{
"success": true
}
The application is secured by an XSUAA service instance. A user who wants to access the endpoint need the scope “Display”. This scope is assigned to my test user via a role collection. In case you want to try it out by yourself you can clone my test repository xsa-access-api-from-external, create the XSUAA service either via CLI or the XSA Cockpit using the xs-security.json file, deploy the test application, create a role collection containing the created roles and assign the role collection to your test user.
In my case I deployed the application to the development space on a HANA express edition instance.
Determine the XSUAA server and the client credentials
Before an access token can be requested it needs to be known where the token can be requested. It is also necessary to know the client id and client secret for the application we deployed before. Both information can be found in the environment variables of the application in following properties:
- VCAP_SERVICES.xsuaa.credentials.clientid
- VCAP_SERVICES.xsuaa.credentials.clientsecret
- VCAP_SERVICES.xsuaa.credentials.url (XSUAA server url which provides the required access token endpoint)
The environment variables for an application can be determined either via the XS CLI or within the XSA Cockpit. Pre-condition: Application is deployed and XSUAA service is bound to the application.
To get the environment variables via the XS CLI for my test application with the name IqdL0TY2Av5-jlbDnal-access-api-js deployed in the development space the XS ENV command is called which produces following output.
In the XSA Cockpit the same information can be found in the Service Bindings area for an application. Marking the XSUAA service binding and pressing “Show sensitive data” displays the required information
Determine access token
After all required information is determined an access token can be requested by doing a POST request to the /oauth/token endpoint for the XSUAA server URL. In that test here the complete URL is http://hxehost:39032/uaa-security/oauth/token.
For test reasons Postman is used to simulate the client. As seen on the next image for the request the following headers have to be set:
- Accept: set to application/json;charset=utf8 to indicate that the client accepts a JSON response
- Content-Type: set to application/x-www-form-urlencoded to define that the payload contains form-url-encoded values
The payload of the request needs to have following form-url-encoded values:
- grant_type: set to password to define that the client and user credentials method has to be used for the token determination
- username: set user name of authorized user
- password: password of the authorized user
- client_id: the client id determined for the application
- client_secret: the client secret determined for the application
- response_type: set to token to indicate than an access token is requested
After a successful call with correct user and client credentials an access token is returned in property access_token. The scope property contains also the Display scope (external-api-access.Display = application name + scope) which is assigned to the user via the role collection containing the relevant role.
Positive API access test
With the access token it is possible to access the test API. The test API endpoint is available at end point /api/testEndpoint for the deployed Node.js application. In that case the full URL is https://hxehost:51047/api/testEndpoint. In case you try the example by yourself check the application URL you got either in the XSA Cockpit (application section) or via the XS APPS command.
For the executed GET request again the Accept header is set to indicate that a JSON response is expected. The more important header is the Authorization header which has to be set in form “Bearer <access token>”.
Executing the request will result in getting the expected JSON object.
Negative API access test
To show that the access token really works and is not just a fake the access token in the header is set to something different than the access token provided by the UAA server. Adding e.g. a “XX” in front of the access token in the header results in a 403 Forbidden result.
Conclusion
I hope this helps a little bit in case someone wanna access an XSA deployed service via an access token from another external application. Please also check the above linked official Cloudfoundry UAA documentation for more details and options.
Hi Florian,
first off thank you for this blog it is rather hard to find resources about the topic in the XS a context. And as a disclaimer I’m pretty new to this stuff. I followed the flow with our XS advanced application and got an access token in Postman, but I’m unfortunately getting a 403 error:
which I find strange I was assuming that having the $XSAPPNAME.Display scope would suffice.
I’m also wondering if the xsuaa is the IdP in your scenario, is that even possible? What I’m curious about is wether the username and password that are passed in the POST call also live in the xsuaa or in an exernal IdP(which is our scenario).
EDIT:
It is resolved now the issue was actually sitting in front of the computer. I was trying to access the wrong endpoint namely the application router, which requires more scopes. Using the access token to access the js module directly yielded the expected result.
Hi Mahmoud,
I am facing a similar issue, can you please specify what you changed to make it work.
Thanks,
Todor
Hi Todor,
if you have a Custom IAS tenant configured to the sub account, make sure both default and custom IAS tenants are marked as active in BTP cockpit->security->trust configuration.
Because by default, it will try to authenticate using SAP.IDS and if it is marked as inactive, it will throw unauthorized issue.
Hi Florian,
Thank you for this blog post. It's very useful for me.
When I try to post to /oauth/token, I get the error:
{
"error": "unauthorized",
"error_description": "Bad credentials"
}
I guess it's because we configure to use SAML with the xsuaa. Could that be the reason that I can't get the token ? If yes, do you have any suggestion how we can get the access token ?
Thank you for your help,
Best regards,
Denise
Hi Denise, I have the same issue. Did you find any solution?
Thanks and BR,
Fabio
Hi Denise,
Were you able to resolve this issue I’m also facing the same issue? Kindly update if you found the solution.
Regards,
Arun
hi any updates on getting the token when we use SAML / SAP ID service .
best regards
rakshetha
Hello Everyone,
This access token retrieval was working fine for me and suddenly I started facing this unauthorized error.
Later, I figured out that my username is wrong.
Three things to pay attention,
Kind regards,
Subash.
Hello,
I was getting the same error message
For me the issue was that I didn’t URL encode the parameters in Postman. To do so do the following:
That will encode special characters like for example “@” in the email for sending in an URL parameter.
After that it worked out.
I am still facing this issue. Any highlight on how to resolve it?
Florian Pfeffer : Great blog!
Hi Priyanka,
I'm also still facing this issue, even with URI encoded parameters and correct credentials. Did you manage to fix it?
Kind regards,
Fabian
Can you help in achieving the same with OAuth+SAML Bearer token? There is one document about SCP Neo to SCP CF - but in our case we are using some other application and struggle with the option of SAML Assertion
Hi Florian,
Thank you for providing this example.
One question I have is how does this work with multiple IdPs? I had difficulty getting this to work with a proper username/password using my own custom IdPs (each provided through the SAP Identity Authentication Service (IAS)) and only had success when re-enabling the default SAP ID Service. Is there some other type of value that can be passed into the request to ensure those other identity stores are taken into account?
**Edit**
I looked at the CF UAA Documentation for password grant and they list a parameter that covers my question exactly. The documentation describes the parameter
login_hint
saying it Indicates the identity provider to be used. I've tried using this in my token request and it appears to have an (unsuccessful) influence on the response. Here's what I'm seeing when it's included:Based on the error I'm thinking the problem here is that a typical SAP IAS IdP does not support password grant out-of-the-box. So far I haven't found any configuration options to enable this.
Any insight on how I might work around this? One lead I'm considering chasing is using OpenID Connect within the SAP IAS but I haven't found how that can be used in SAPCP CF Subaccount Trust Configuration.
Thanks,
Brian
Hello,
password grant_type is not supported with custom IdP, as documented at https://launchpad.support.sap.com/#/notes/2766354
Regards,
Binson
Binson,
Thank you for the response. My mistake for not checking SAP Notes and instead thinking this sort of thing would be documented within the publicly accessible documentation. Always difficult when there are multiple sources of documentation!
Thanks,
Brian
The blog is amazing! Thank you so much.
Excellent post! very helpful and precise!
I have extended the OAuthClient to also use client credentials flow:
https://gist.github.com/scanacs-awuttig/0f1249e279690be97c297110b37d5e10
can i generate a GWT?
Hi,
Great Blog!
Can i generate token by providing user name and password rather providing client id and client secret with custom IDP service?
Thanks,
Rajdeep Bhuva
Hi Rajdeep,
have you finally found how to gen token by providing user name and password? I have the issue.
Ok Rajdeep,
but in my situation i need to gen token using password credential. I see in many blog that is not possible due to custom IDP not support it. Do you know if exists a possible solution that avoid this problem?
Tnks
Francesco
Hi Francesco Pisano ,
Sorry, No solution exists.
You can pass userID in parameter with encoded value and make it somewhat level difficult and achieve it.
Thanks,
Rajdeep Bhuva
Hi Florian Pfeffer ,
When i tried to send the POST request, there was no access token returned.
Initially i had bad credentials error returned and i solved it by EncodeURIComponent as suggested above.
However now, this was the return:
Would you know what could've possibly been missed?
Thank you very much.
Hello Florian Pfeffer ,
Thanks a lot for this guide!
Everything worked fine for me as long as I was working with Postman. When I tried to consume the API in a chatbot with URL https://xyz.dev.azure.com, I got error 403 for POST call for getting the access token.
Basically, the error was for the preflight call.
Can you suggest what needs to be fixed?
Thanks & regards,
Pooja Surve