SAP Cloud Platform Security:
SAP_JWT_TRUST_ACL
is obsolete
and doesn’t work anymore
since May 2020 (roughly)
Quicklinks:
Quick Guide
Sample Code
Intro
I assume you’ve found this blog post because you’re troubleshooting an error or because you’ve found this setting in an app and wondering about it
Personally, in my case, I was searching for solution of a problem – and found below information.
Since this variable
SAP_JWT_TRUST_ACL is broadly used is docu, blog posts, applications etc, I thought it makes sense to share below information with you.
Please note that I’m not a security expert and please accept my apologies for vague and imprecise explanations.
Overview
For better understanding, we're going through a long tutorial, describing why the setting was necessary and why it is not needed anymore
Part 1:
Simple scenario
Hands-On 1: app2app with one instance of XSUAA
Part 2:
Scenario requires SAP_JWT_TRUST_ACL
Hands- on 2: app2app with 2 instances of XSUAA -> error
Part 3:
Same scenario with new mechanism
Hands- on 3: app2app with 2 instances of XSUAA -> solved
Background
In
SAP Cloud Platform, we use OAuth 2.0 to protect applications
In general, security is a huge and advanced topic.
In this blog post, I’m focusing on a small portion:
security between applications
With other words:
app2app scenario
With other words:
client-credentials
To establish security for an application, an application developer has to do the following:
- Create an instance of XSUAA
- Bind the instance to the application
- Write some security-related code
More details:
- XSUAA acts as “Authorization Server” (in terms of OAuth) which generates a JWT token
- This access token is used to call the secured app
- The secured app is bound to an instance of XSUA
- The binding information contains e.g. the “client ID”, "xsappname" etc
- The app uses that info to validate the JWT token
- Properties "clientid" and "secret" are used as credentials for contacting the “Authorization Server” (xsuaa)
Part 1: Simple app-to-app scenario
We can call it app2app or app2service, just to make clear that this is not user centric scenario, no interactive user-login, no roles, no role collections, no Identity Provider
No friendly ppl
Only apps
Cool aps
Scenario 2: app-to-app with 1 instance of XSUAA
The flow:
“We” call the authorization server with client/secret and as response, we get a JWT token
“We” use this token to call the protected app
The protected app validates the token and is happy to send a success- response
What is that mystic JWT token ?
To make it even more mystic, it is pronounced as "jott token" (you must have heard that)
The token is a long trashy string consisting of 3 encoded parts, separated with dots
The middle part is the payload, JSON formatted
The payload contains info about
- client ID,
- scopes (roles)
- user eMail
- audiences,
- identity zone
etc
Validation
The protected app is protected because it validates the incoming access token
Validation is mostly done by the
xssec client library, provided by SAP
The xssec takes the JWT token, decodes, parses and checks it (so-called
offline validaton)
One of the first things to check:
The client ID contained in the token: is it the same like the client ID of the bound xsuaa instance?
In our simple example flow, yes, it is the same:
To get the JWT, we’ve contacted the same xsuaa instance, which is bound to the protected app
Above diagram shows:
We have 2 applications, bound to one instance of XSUAA
The provider app is provides a protected endpoint which requires a valid JWT token
The client app consumes the endpoint
To do so, the client app uses binding info to contact XSUAA to get a JWT token
To access the endpoint, the client app sends the JWT token to the provider app
To validate the token, the provider app uses binding info
Hands-on 1: app2app with one instance of XSUAA
This hands-on is useless, but nevertheless:
Let's build that simple scenario flow. The code can be found in the
appendix 2
Note that only the security configuration is different, you need to copy it from here
1. Security Configuration
We create an instance of XSUAA with very basic params.
As good practice, we store the params in a securitydescriptor file, typically called
xs-security.json, but in my tutorials I call it differently, today the file is called
xs-securityforprovider.json
{
"xsappname" : "xsappforprovider"
}
Note that I like to use stupid names.
You've already noticed that file names are stupid
Now properties are stupid
In this case, I want that the name makes clear that it is the value of the property "xsappname"
Now create instance based on above stupid param:
Jump into the folder
C:\tmp_app2app\providerapp and execute
cf cs xsuaa application xsuaaforprovider -c xs-securityforprovider.json
2. Provider App
Afterwards we can create the provider app according to the sample code in the
appendix 2
3. Client App
The client app has one difference compared to the sample code in the
appendix 2
The difference is that in our first hands-on we use the same instance of xsuaa.
As such, the service name in the manifest has to be adapted
---
applications:
- name: clientapp
memory: 128M
buildpacks:
- nodejs_buildpack
services:
- xsuaaforprovider
4. Deploy and Run
After deploying both apps (in my example, I deployed to my Trial account), we call the endpoint of our client app
https://clientapp.cfapps.eu10.hana.ondemand.com/trigger
The clientapp code uses the client ID from xsuaa credentials from binding to get a JWT token
The provider app prints some boring but relevant info to the logs:
What do we see here?
The first line prints the value of the variable
xsappname from the
xs-security.json file
We can see that this name is used when generating a client ID
We can see that the client ID which is used by the provider app is the same as the client ID sent by the client app in the JWT token
This doesn’t surprise us, because we know that the JWT token has been issued by the same instance of xsuaa
We can see that there aren’t specific scopes/authorities (we haven’t defined)
Finally, the interesting property: "audiences". We can see that the client app sends the client ID in the audience property of the JWT token
Why interesting?
We’ll come to it later
How boring
5. Ehm??
Any problem?
No problem.
Only that we haven't talked at all about that JWT_WHATEVER_TRUST???
True.
Why was SAP_JWT_TRUST_ACL required?
It was required in a scenario like the following one:
Part 2: Scenario requiring (legacy) SAP_JWT_TRUST_ACL
To answer this question, let’s have a look at a scenario with 2 instances of XSUAA
Why are there 2 instances of XSUAA?
There can be several reasons:
E.g. the provider app can be a user-defined micro-service
Or the provider app can be a re-use service provided centrally in your tenant
Or the provider app can be living in a different account (see
here)
Scenario 2: app2app with 2 instances of XSUAA
In this scenario we can expect problems
Brrrrr....
Reason:
The client app will obtain a JWT token which contains a new and different client ID, because it is issued by a different instance of xsuaa
The provider app will validate the JWT token and will find a client ID which is unknown to it
Let's see it in concrete example
Hands- on 2: app2app with 2 instances of XSUAA -> error
1. Provider App
No change at all
2. Client App
Define Security Configuration
Now we need a security configuration for the client app
We create a file with (stupid) name
xs-securityforclient.json in folder
C:\tmp_app2app\clientapp
We don't copy the content from the appendix.
Instead, we use the following (useless) content:
{
"xsappname" : "xsappforclient"
}
Then we create the second instance of XSUAA:
cf cs xsuaa application xsuaaforclient -c xs-securityforclient.json
Modify Client App
Afterwards we change again the
manifest.yml file, because now we want to use this new instance:
---
applications:
- name: clientapp
memory: 128M
buildpacks:
- nodejs_buildpack
services:
- xsuaaforclient
Before we push the app, we should delete the existing app (or delete the binding)
Reason:
Since we deployed the client app with a binding, the new binding would be added, which would cause trouble.
So let's just delete the deployed app
cf d clientapp -r -f
Then push again
3. Run the apps
Now, when calling our client app
https://clientapp.cfapps.eu10.hana.ondemand.com/trigger
Result:
Error
The request is rejected by xssec
Reason: In the log, we can read the reason:
[ERR Jwt token with audience: [ 'uaa', 'sb-xsappforclient!t53896' ] is not issued for these clientIds: [ 'sb-xsappforprovider!t53896', 'xsappforprovider!t53896' ]
And this is (finally) the first relevant learning of this blog post:
The security library checks if the incoming JWT token contains the client ID of its own binding
If it is different, the access to the resource is forbidden, the call is not allowed
Now we check our own log output:
We can see:
The client ID of the protected app is:
sb-xsappforprovider!t53896
The client ID contained in the JWT, sent by clientapp is
sb-xsappforclient!t53896
They're different
Obviously
However:
The scenario is legal and necessary.
In every scenario where an application has to call a re-use service and where the client app cannot be bound to the same instance of xsuaa
As said above
The Solution (Legacy)
The solution until now was:
The provider app had to define a white-list where the allowed consumers were listed
This list (ACL) was provided as environment variable of the application in Cloud Foundry
The name of the env variable was the well known
SAP_JWT_TRUST_ACL
The value was a list of client IDs, and also different identity zone IDs could be maintained
Remember: ACL stands for “Access Control List”
I guess we all felt a bit strange when learning this concept – it somehow looked a bit odd.
Why odd?
Odd, because it was a different, unexpected concept: security was defined in the
xs-security.json file and ACL (security-relevant as well) was defined somewhere else
Also, when transporting an app to different landscape (dev->prod) then the admin mustn’t forget to adapt the ACL env var to new client IDs etc
As such, it has been a good idea to replace that mechanism
Agree
Part 3: The new mechanism
The question is:
How to declare that a foreign client is allowed to access my protected resource?
The answer is:
Using the
"aud" claim
What’s that: the aud claim ???
This is one of the so-called “claims” contained in a JWT token
What’s that: claim ???
As we know, the JWT token is a very small package carrying compact information, designed to be small and easy to consume
That’s why it is encoded and JSON-formatted
A JSON object consists of properties and in case of JWT, these properties are called “claims”
They’re like statements about the desired access of the “bearer” (e.g. the user or client)
Some of the claims contained in a JWT token are default, predefined, so-called “registered claims”, others are added by SAP, are "public claims"
Since the JWT is meant to be small, the claim names are small as well, reduced to 3 characters
In the
appendix 1, I’ve put together some useful info about claims
claims are e.g.:
- iss: the issuer of the JWT token
- iat: issued at, so we can know if the token is getting old
- sub: subject, the owner of the token, in our case the client ID
- exp: the expiration time
All that makes sense, right?
Yes
And also the predefined
aud, which stands for “audience”
This claim contains the info about who is meant to receive the token
Example scenario:
You have a “cat-snack”, this is the “token” with tasty ID inside
And there is a cat, watching the snack: that’s the “audience”
Close your eyes
Open your eyes
Now the ID is in the audience
And: scenario works only if the cat is in a good mood and "accepts" the token
OK?
Another try:
In the JWT token, ...
...the "client ID" is the consuming app which sends the token
...the "audience" is the providerapp which receives and validates the token
Validation:
The provider app validates the JWT token (with the help of the xssec lib)
Now that a foreign Client ID is not maintained in the TRUST_BULLSH...ACL anymore, it has to be maintained in the
audience
Let’s have a look again at the failing scenario above:
The providerapp receives a JWT token with audience as
...xsappforclient...
This is something totally unknown
As such the access is refused
Fix:
We have to somehow get the
...xsappforprovider into the
aud
Yes, but how?
How to maintain the required aud ???
We cannot maintain it like we maintained the ACL variable
Let’s phrase it positively:
We don’t NEED to maintain it anymore
That’s an advantage
😉
The aud claim is automatically filled by XSUAA
So how to solve our problem?
We can influence:
The additional entries in aud are derived from scopes
Scopes???
Scope is a powerful mechanism to control how can access what resource and functionality
Example:
Reading content of an API is allowed for many users, while deleting an entry is allowed only for admins
In case of human users, they get a “role” assigned
In case of apps, they get a scope “granted”
The protected app will check if the JWT token contains the required scope
In our scenario, an API provider app is called by a consuming app (hot human user)
If the provider app requires a scope, then the client app needs a “grant”
I described all that mechanism in
this blog post
As such, the provider app needs to know the client, so it can grant the scope to it
If the client has that scope, then it is allowed to call the providerapp
OK, that’s quite obvious
And because it is so obvious, it is possible to automatically enter the
...providerapp... into the audience
Surprised?
It is easy
With other words:
If
provider app grants "provider"-scope to client
then
client app gets JWT token with “provider” in the aud
Nice, but how to do the “grant” thing?
This is done while configuring the involved instances of XSUAA
Configuration is done with params that are usually stored in a file with common name
xs-security.json
The name can be arbitrary, as it is passed to the create or update command
When we created the XSUAA instance for providerapp above, we didn’t enter anything relevant:
{
"xsappname" : "xsappforprovider"
}
The provider app was protected, but didn’t require a scope
Now we need to define a scope
In addition, we need to
grant that scope to the consuming app
"scopes": [{
"name": "$XSAPPNAME.scopeforprovider",
"grant-as-authority-to-apps" : [ "$XSAPPNAME(application, xsappforclientapp)"]
And one more step is required:
The client app has to explicitly
accept the granted scope
"authorities":["$XSAPPNAME(application,xsappforprovider).scopeforprovider"]
And the result?
As mentioned, as soon as the consuming client app gets the scope, it also automatically gets the audience
But there’s an important detail
The scope name is not completely filled into the
aud claim
The
scope is not completely equal to the
audience
The whole mechanism relies on one fact:
When defining the scope, we concatenate the
$XSAPPNAME with a dot and with the scopename
"name": "$XSAPPNAME.<scopename>"
What is $XSAPPNAME ?
Basically, it is the value of the property
xsappname which we define as we like and we can refer to it to avoid typos
"xsappname" : "xsappforproviderapp",
"scopes": [{
"name": "$XSAPPNAME.scopeforproviderapp",
But it is more:
XSUAA generates a full xsappname at runtime, we’ve seen it in the screenshot above:
xsappforprovider!t53896
We cannot know the fully generated name, as such, we MUST use the variable when concatenating the scope
At runtime, the full scope name will look like this:
xsappforprovider!t53896.scopeforprovider
Why we must concatenate at all?
First of all, this mechanism makes the scopes unique.
Furthermore, to generate the
audience, the scopename is removed, so basically the
$XSAPPNAME is put into the
aud
Note:
Scope names can contain more dots, e.g. to add namespaces
So the audience can be longer
But the xssec takes care of properly validating and taking dots into account
Note:
Scope names aren’t forced to use the naming convention
$XSAPPNAME.<scopename>
However, if we don’t do that, we cannot get this scenario running
Example:
Go into town center and show
$$
You will immediately have
audience
To phrase it differently:
No $ no aud
Small Recap
In the protected provider app we define a
scope, which is required for access
We also
grant the scope to the client app
In the client app, we
accept the grant
As a consequence, the
JWT token, which is sent by the clientapp to the providerapp, will have the providerapp in the
aud
As final consequence, the access is allowed
And remember: no $ no aud
Hands- on 3: app2app with 2 instances of XSUAA -> solved
To fix the scenario in the new way, we have to modify the 2 security descriptors and update the instances of XSUAA
1. Modify XSUAA of provider app
Modify the security descriptor
xs-securityforprovider.json
in folder
C:\tmp_app2app\providerapp
Add scope and grant
{
"xsappname" : "xsappforprovider",
"scopes": [{
"name": "$XSAPPNAME.scopeforprovider",
"grant-as-authority-to-apps" : [ "$XSAPPNAME(application, xsappforclient)"]
}]
}
Now modify the service instance:
Jump into the folder "providerapp" and run the following command
cf update-service xsuaaforprovider -c xs-securityforprovider.json
2. Modify XSUAA of client app
Add "authorities" statement to file
xs-securityforclient.json in folder
C:\tmp_app2app\clientapp
{
"xsappname" : "xsappforclient",
"authorities":["$XSAPPNAME(application,xsappforprovider).scopeforprovider"]
}
Now modify the service instance:
Jump into the folder "clientapp" and run the following command
cf update-service xsuaaforclient -c xs-securityforclient.json
3. No deploy but run
After updating both instances of XSUAA, we can assume that we get a “better” JWT token (tasty cat food)
No change to the applications required
So we can invoke the client app
https://clientapp.cfapps.eu10.hana.ondemand.com/trigger
Enjoy the success message...
And view the logs:
We can see 2 interesting changes:
The bearer token now contains the granted scope
The
aud now contains the
$XSAPPNAME of the providerapp
When the providerapp receives the JWT token and xssec validates the audience, it finds its own
$XSAPPNAME
This is not unknown, thus happy to go through the security control
That’s all for today.
Conclusion
We MUST provide a scope
We MUST use the variable
$XSAPPNAME in scope name
We CAN remove the variable
SAP_JWT_TRUST_ACL from your env (it is ignored anyways)
A scenario where only authentication with no authorization is desired: that’s not possible anymore
Quick Guide
Scenario:
Secure communication from one app to second app
Procedure:
Second app defines scope and grants it to first app
"scopes": [{
"name": "$XSAPPNAME.scopeforprovider",
"grant-as-authority-to-apps" : [ "$XSAPPNAME(application, xsappforclient)"]
First app accepts the grant in "authorities"
"authorities":["$XSAPPNAME(application,xsappforprovider).scopeforprovider"]
Result:
JWT token contains the target
$XSAPPNAME in the audience claim which is validated by target app
Environment variable
SAP_JWT_TRUST_ACL is NOT required anymore
Links
Related info
OAuth intro
OAuth flow with REST client
App to app security
App to app security accross subaccounts
JWT
How to add custom properties to JWT
jwt.io
View content of jwt token:
https://jwt.io/ -> Debugger
Spec
More
spec
Even more
spec
SAP Help Portal
Security Descriptor Syntax
Granting scope access to different app
TechEd self-study material
GitHub
Node client modules at npm
xssec
xsenv
Java libraries at github
Cloud Security XSUAA integration
Security Glossary.
Appendix 1: claims
For your convenience, I’ve listed claims and explanations.
The content is copied from the specs (see links section)
The list contains
registered claims and
public claims
jti
The "jti" (JWT ID) claim provides a unique identifier for the JWT.
The identifier value MUST be assigned in a manner that ensures that there is a negligible probability that the same value will be accidentally assigned to a different data object; if the application uses multiple issuers, collisions MUST be prevented among values produced by different issuers as well. The "jti" claim can be used to prevent the JWT from being replayed. The "jti" value is a case-sensitive string. Use of this claim is OPTIONAL.
sub
The "sub" (subject) claim identifies the principal that is the subject of the JWT. The claims in a JWT are normally statements about the subject. The subject value MUST either be scoped to be locally unique in the context of the issuer or be globally unique.
The processing of this claim is generally application specific. The "sub" value is a case-sensitive string containing a StringOrURI value. Use of this claim is OPTIONAL.
scope
The value of the "scope" claim is a JSON string containing a space-separated list of scopes associated with the token
client_id
The "client_id" claim carries the client identifier of the OAuth 2.0 client that requested the token.
azp
Authorized party - the party to which the ID Token was issued
iat
The "iat" (issued at) claim identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.
exp
The "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. The processing of the "exp" claim requires that the current date/time MUST be before the expiration date/time listed in the "exp" claim. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.
iss
The "iss" (issuer) claim identifies the principal that issued the JWT. The processing of this claim is generally application specific. The "iss" value is a case-sensitive string containing a StringOrURI value. Use of this claim is OPTIONAL.
aud
The "aud" (audience) claim identifies the recipients that the JWT is intended for. Each principal intended to process the JWT MUST identify itself with a value in the audience claim. If the principal processing the claim does not identify itself with a value in the "aud" claim when this claim is present, then the JWT MUST be rejected. In the general case, the "aud" value is an array of case-sensitive strings, each containing a StringOrURI value. In the special case when the JWT has one audience, the "aud" value MAY be a single case-sensitive string containing a StringOrURI value. The interpretation of audience values is generally application specific.
Use of this claim is OPTIONAL.
And:
Other properties which are available in a user centric scenario:
Logon name
Given name
Family name
email
For your convenience find below an example for the payload of a JWT token in my example in Trial account
{"jti":"1234567847b448d79d3d00c14a733265",
"ext_attr":{
"enhancer":"XSUAA",
"subaccountid":"1234abcd-bdb9-42dd-a563-fce0ccb976bc",
"zdn":"1234trial"},
"sub":"sb-xsappforprovider!t53896",
"authorities":["uaa.resource"],
"scope":["uaa.resource"],
"client_id":"sb-xsappforprovider!t53896",
"cid":"sb-xsappforprovider!t53896",
"azp":"sb-xsappforprovider!t53896",
"grant_type":"client_credentials",
"rev_sig":"6fbad123",
"iat":1598964507,
"exp":1599007707,
"iss":"
https://1234trial.authentication.eu10.hana.ondemand.com/oauth/token",
"zid":"1234abcd-bdb9-42dd-a563-1234abcd 76bc",
"aud":["sb-xsappforprovider!t53896","uaa"]}
Appendix 2: All Sample Project Files
For your convenience, see screenshot for overview about project structure
App 1: API Provider App
xs-securityforprovider.json
{
"xsappname" : "xsappforprovider",
"scopes": [{
"name": "$XSAPPNAME.scopeforprovider",
"grant-as-authority-to-apps" : [ "$XSAPPNAME(application, xsappforclient)"]
}]
}
package.json
{
"main": "server.js",
"dependencies": {
"@sap/xsenv": "latest",
"@sap/xssec": "latest",
"express": "^4.16.3",
"passport": "^0.4.1"
}
}
server.js
const express = require('express');
const passport = require('passport');
const xsenv = require('@sap/xsenv');
const JWTStrategy = require('@sap/xssec').JWTStrategy;
const xsuaaCredentials = xsenv.getServices({ myXsuaa: { tag: 'xsuaa' }}).myXsuaa;
passport.use(new JWTStrategy(xsuaaCredentials));
const app = express();
// Middleware to read JWT sent by JobScheduler
function jwtLogger(req, res, next) {
console.log('===> Binding: $XSAPPNAME: ' + xsuaaCredentials.xsappname)
console.log('===> Binding: clientid: ' + xsuaaCredentials.clientid)
console.log('===> Decoding JWT token sent by clientapp' )
const authHeader = req.headers.authorization;
if (authHeader){
const theJwtToken = authHeader.substring(7);
if(theJwtToken){
const jwtBase64Encoded = theJwtToken.split('.')[1];
const jwtDecoded = Buffer.from(jwtBase64Encoded, 'base64').toString('ascii');
const jwtJson = JSON.parse(jwtDecoded)
console.log('===> JWT: audiences: ');
jwtJson.aud.forEach(entry => console.log(` -> ${entry}`) );
console.log('===> JWT: scopes: ' + jwtJson.scope);
console.log('===> JWT: authorities: ' + jwtJson.authorities);
console.log('===> JWT: client_id: ' + jwtJson.client_id);
}
}
next()
}
app.use(jwtLogger)
app.use(passport.initialize());
app.use(passport.authenticate('JWT', { session: false }));
app.get('/protected', function(req, res){
res.send('The endpoint was reached, not authorization check');
});
app.listen(process.env.PORT || 8080, () => {})
manifest.yml
---
applications:
- name: providerapp
memory: 128M
buildpacks:
- nodejs_buildpack
services:
- xsuaaforprovider
env:
DEBUG: xssec:*
App 2: Client App
xs-securityforclient.json
{
"xsappname" : "xsappforclient",
"authorities":["$XSAPPNAME(application,xsappforprovider).scopeforprovider"]
}
package.json
{
"dependencies": {
"express": "^4.16.3"
}
}
server.js
const express = require('express')
const app = express()
const https = require('https');
const VCAP_SERVICES = JSON.parse(process.env.VCAP_SERVICES)
const CREDENTIALS = VCAP_SERVICES.xsuaa[0].credentials
// endpoint of our client app
app.get('/trigger', function(req, res){
doCallEndpoint()
.then(()=>{
res.status(202).send('Successfully called remote endpoint.');
}).catch((error)=>{
console.log('Error occurred while calling REST endpoint ' + error)
res.status(500).send('Error while calling remote endpoint.');
})
});
// helper method to call the endpoint
const doCallEndpoint = function(){
return new Promise((resolve, reject) => {
return fetchJwtToken()
.then((jwtToken) => {
const options = {
host: 'providerapp.cfapps.eu10.hana.ondemand.com',
path: '/protected',
method: 'GET',
headers: {
Authorization: 'Bearer ' + jwtToken
}
}
const req = https.request(options, (res) => {
res.setEncoding('utf8')
const status = res.statusCode
if (status !== 200 && status !== 201) {
return reject(new Error(`Failed to call endpoint. Error: ${status} - ${res.statusMessage}`))
}
res.on('data', () => {
resolve()
})
});
req.on('error', (error) => {
return reject({error: error})
});
req.write('done')
req.end()
})
.catch((error) => {
reject(error)
})
})
}
// jwt token required for calling REST api
const fetchJwtToken = function() {
return new Promise ((resolve, reject) => {
const options = {
host: CREDENTIALS.url.replace('https://', ''),
path: '/oauth/token?grant_type=client_credentials&response_type=token',
headers: {
Authorization: "Basic " + Buffer.from(CREDENTIALS.clientid + ':' + CREDENTIALS.clientsecret).toString("base64")
}
}
https.get(options, res => {
res.setEncoding('utf8')
let response = ''
res.on('data', chunk => {
response += chunk
})
res.on('end', () => {
try {
const jwtToken = JSON.parse(response).access_token
resolve(jwtToken)
} catch (error) {
return reject(new Error('Error while fetching JWT token'))
}
})
})
.on("error", (error) => {
return reject({error: error})
});
})
}
// Start server
app.listen(process.env.PORT || 8080, ()=>{})
manifest.yml
---
applications:
- name: clientapp
memory: 128M
buildpacks:
- nodejs_buildpack
services:
- xsuaaforclient