Technical Articles
SAP Cloud Platform: How to call – onPrem System – from Node.js app – via Cloud Connector
You have a Node.js application running in SAP Cloud Platform.
You want to call an API located on your onPremise system (e.g. S4, etc)
You want a sample based on Node.js
—> To get started, you might find it useful to go through this tutorial
Quicklinks:
Quick Guide
Featured Chapter
Sample Code
The scenario:
Prerequisites
- Access to SAP Cloud Platform, some basic knowledge,
e.g. how to deploy applications, using CLI - Node.js installed on local machine + some knowledge
- Back-end is not required: we simulate it with local server on local laptop
- Access to local laptop
Overview
- Preparation on local laptop:
– create app to simulate back-end
– install Cloud Connector - Preparation in Cloud
– create instance of destination service and configure destination
– create instance of connectivity service - Write Node.js application
– create and implement app
Note:
The focus of this tutorial is how to call the Connectivity service from node.js app
Understand what’s happening
So we can easily replace the backend with a little mock on our laptop
This mock doesn’t even serve an OData service, only a REST endpoint
If you have a real backend OData service, you can check out the SAP Cloud SDK for JavaScript for calling it
Preparation 0: The Local Project
In our tutorial, we’re going to use 3 working directories:
– Local node.js project
– Local installation of SAP Cloud Connector
– Node.js project for app to deploy to cloud
Create root folder for all 3 working directories: C:\tmp_onPrem
Preparation 1: The Back-end Mock
To be able to follow this tutorial, we need an onPrem back-end
With other words, we need a server with an endpoint which is not in the cloud
This can be easily simulated with a mock server, running locally on our laptop
So let us quickly create that mock server, we just need to write a little Node.js app which exposes a dummy endpoint
1. Create mock server
Create working directory C:\tmp_onPrem\backendmock,
create files like shown in Appendix 1
copy the file contents
On command line, jump into the folder backendmock and execute npm install
This will install the express module
2. Start mock server
On command line, jump into folder backendmock and execute node server.js
This will start up the server
3. Run local endpoint
Open a browser of your choice and call the following address:
http://localhost:8080/products
This is the port and endpoint as written in our back-end-mock-server-app
The result is disgusting: we get an error on our own local app…!
But this is not surprising, because we’ve added a little silly authentication strategy to our code:
const auth = req.headers.authorization || ''
const user = Buffer.from(auth.substring(6), "base64").toString('utf8')
if(user === 'john:secret'){
. . .
Yes, it is silly code, because we don’t use passport middleware, etc –yeeees— but this way is simpler and I don’t mind if a silly back-end has silly source code
Now open a REST client of your choice and enter user credentials (of the only one permitted user) as Basic Auth:
user: john
password: secret
Then invoke the endpoint with the REST client and check the response
As a result, you can see the list of products.
A silly small list, what a shame
Finally, now we have an onPremise back-end
And it is (somehow) “protected'” with basic auth
…I know it makes you laugh…;-)
Preparation 2: SAP Cloud Connector
Please forgive that we cannot go in detail through the required steps.
Everything is well explained in the official SAP Help Portal
The focus of this tutorial is in the implementation of the cloud app.
Nevertheless, I’ve taken some screenshots and pasted them in Appendix 3
1. Install SAP Cloud Connector
To download the portable zip, go to
https://tools.hana.ondemand.com/#cloud
scroll down to “Cloud Connector”
and choose the installation package.
In my case it was this link:
https://tools.hana.ondemand.com/additional/sapcc-2.12.4-windows-x64.zip
It can be extracted to any folder of your choice.
In my example I’ve created a subfolder of our project root:
C:\tmp_onPrem\CloudConn
And extracted the zip there. Nothing else to do for installation
2. Start Cloud Connector
To start the Cloud Connector, open command prompt and jump to the installation folder.
Execute go.bat and wait for final log statement
Then we can open the following URL in our browser:
https://localhost:8443/
Initial credentials: Administrator / manage
3. Configure Cloud Connector
First thing to do is some very basic config:
Cloud Conn wants us to change the password, so we change it
We leave the installation type as “master”
We press “Save” in the upper corner
Now we start with the real configuration.
Don’t worry, for our simple scenario it isn’t complicated
3.1. Plug the Cloud
Cloud Connector is used to connect onPremise systems to the cloud
We see: 2 parties to be connected. Both need to be plugged into Cloud Connector
First, let’s specify the “cloud”-side a little bit more concrete
What we need to enter is some info of the Subaccount into which we want to plug
Region
In my case, I’ve chosen eu10
Subaccount
This is not the name of the Subaccount. Instead, we have to search for the ID
We can find the Subaccount ID in the cockpit, on the overview page of the subaccount
Display Name
This can be the name of the subaccount. The value entered here will appear in the Cloud Connector UI
Login E-Mail + Password
The credentials of the cloud platform user
Location ID
This is only necessary, if there are multiple Cloud Connectors connected to the same subaccount
Let’s enter a value here, to make it little bit more complicated
Also, it allows us to not get in trouble which our admin, who configured a real professionally used cloud connector for our subaccount
After saving the configuration, we get an info message which is related to the locationID and which we can ignore
Now we can check if our Cloud Connector has been recognized by the cloud platform
We go to the cloud cockpit, then “Subaccount-> Connectivity->Cloud Connectors”
Here our local Cloud Connector should be displayed
3.2. Plug the onPrem
Now that the Cloud Connector is connected to the cloud, we can take care of the other side:
the onPrem side.
In our case, we want to plug our local mock server into Cloud Connector
In Cloud Connector UI, we click on “Cloud To On-Premise” on the left menu
We press the + icon to create a new “Mapping Virtual to Internal System”
Back-end Type
As Back-end Type we choose “Non-SAP-System”
HTTP
yes, “HTTP”
Internal Host
Next, we have to enter the “internal host”. This is the “real” host, the real back-end system.
The one which we can touch with our browser, the one which we can enter in the browser Address bar:
Internal Host: localhost
Internal Port: 8080
Virtual Host
This one is used to hide the real host name. So let’s hide it and no colleague who looks at the cloud cockpit, can assume that our silly local cloud connector is used and that the connected onPrem system a “fake”
Virtual Host: s4hmock
Virtual Port: 3000
Principal Type
None
Host In Request Header
Use Virtual Host
Description
<empty>
After finishing the wizard, we have to do one last configuration:
which Resources are allowed to be accessed (and forwarded) by the cloud connector?
We press the + icon of the “Resources” section and specify:
URL path
We enter just a slash: /
This means that all resources on our server can be exposed
Access Policy
Here we select: “Path and all Sub-Paths”
Finally, we can check the connection by pressing the “Check availability” icon (see screenshot in appendix)
So now our dumb mock server is plugged into the Cloud Connector
At the end of this preparation section, we can again have a look at the cloud cockpit
Now our local mock server has been added in section “Exposed Back-End Systems” and is marked as green, saying that resources are available
Don’t you agree that this is cool:
Our silly mock server is prominently appearing, visible for everybody, in the subaccount
3.3. Recap
We’ve connected our Cloud Connector to the cloud and to our local server
Now we can go ahead and start with the tutorial
Preparation 3a: Destination Service
The tutorial doesn’t really start here: this is another preparation section
It is a recommended and common and best practice to use destinations when calling URLs from any application in the Cloud Platform
1. Create instance of Destination Service
If not already done, we need to create an instance of destination service
In my example, I’ve called it destinationinstance
2. Create Destination Configuration
Now we can create a destination configuration.
Here we enter the URL of the target that we want to call, along with credentials of a (technical) user who has permission to consume the back-end service URL
(Here it would be a good option to use Principal Propagation. But that’s a different topic and we don’t need it in our scenario)
We have to pay attention how to configure the destination:
Name
Here we don’t need much attention. This is a name of our choice.
The configuration has a name and we’ll need the name later, in order to retrieve it
TYPE
The type of destination is HTTP
Description
We can leave it empty
URL
Here we don’t enter the URL of the back-end service. This is important.
The back-end system is not reachable from the cloud
That’s why we configured the Cloud Connector and that’s why we have a virtual system exposed by the Cloud Connector
Remember it?
So that’s what we have to enter here: the virtual URL
It makes sense to enter only the host, not a full URL, because that way we’re more flexible if we want to call multiple endpoints of the back-end
So finally we enter the values which we copy from our Cloud Connector Admin UI:
http://s4hmock:3000
Proxy Type
Here we have to choose onPremise. The other option wouldn’t require a Cloud Connector
Authentication
Remember that in our back-end service mock, we did little silly simulation of a basic authentication? There’s a little check for an authenticated user in the code. That hard-coded user is the one which we have to hard-code here again.
As such, we choose Basic Authentication and enter the
user name as john and the
password as secret
Note: it is a valid scenario to create a user in the back-end which is not a real user, but which has only very restricted roles, required to call one API. That’s a so-called “Technical User”, which can be used to call the back-end API. This is not useful for all scenarios, because often we want that the end-user who logs into the cloud, would also be forwarded to the back-end. But in our tutorial, there’s no data specific for users, so a technical user is fine
Location ID
This is the place to enter the location id which we configured in the Cloud Connector: mylocalmachine
If there are multiple Cloud Connectors connected to the subaccount, this is the way how the correct one is identified
That’s it.
We can save the destination configuration
There’s a little helper button which tries to check the connection
The “Check Connection” will mostly be green. But note that this isn’t a “real” end-to-end check.
The check fails if the Cloud Connector or the back-end are offline. Also it fails if the virtual host would be deleted in Cloud Connecto
But it would still be green if the endpoint would be broken, user invalid etc.
It is just a connection check
And there’s no better check.
Note:
We cannot invoke http://s4hmock:3000/products
Why not?
Hey, we know that it is only a “virtual” host, it exists only in our imagination
It only becomes reality in conjunction with a proxy server
How to get that proxy server?
It is provided by the connectivity service
So that’s our next step
Preparation 3b: Connectivity Service
Still a preparation step – but important
However, it is easy to remember:
Whenever we want to call an onPrem system, we need the Connectivity service
Because onPrem needs to be connected
It is like plugging a laptop to the cloud
The Cloud Connector is the cable
The Connectivity Service is the plug socket
OK?
(Silly comparison…)
1. Create instance
Again, this step is easy. Just create an instance of connectivity service
No params etc required
No configuration required
2. Use it
This step requires more effort.
Using the Connectivity Service means to call the proxy server programmatically
That’s not a preparation step
That’s the ACTUAL content of this tutorial
Which title did I choose for this tutorial?
It must have been meaningful like:
“How to use Connectivity Service”
Or
“How to use Connectivity Service in node.js application”
Any other title would have been silly
OK, now let’s do it.
Step 1: Create Node.js App
The tutorial finally starts with step 1.
We want to create a Node.js app which calls the silly endpoint of our dumb mock server to fetch useless data.
This app is meant to run in the cloud, use the destination configuration, then call the virtual host on Cloud Connector, which finally forwards the request to our mock server
But there’s a piece missing in the picture
The missing piece:
How to call the virtual host?
As we realized: it is not real
And here comes the Connectivity Service with its proxy server
Now the scenario is complete
This has been an overview
Not a real “step 1”
Must have been just another fake..
Step 1: Create Node.js App
Now we can start the tutorial
Really.
We create a small standard node app inside a new working directory below our root project folder
C:\tmp_onPrem\cloudToOnprem
See the folder structure marked in the screenshot below
The app folder contains only the minimum files required to deploy a node app to the Cloud Foundry environment of SAP Cloud Platform
The full content of the files can be found in the Appendix 2
Step 2: Implement App <———- Featured ;-)) ?
1. manifest.yml
In the manifest file we declare the usage of the instances of destination service and connectivity service which we created above
services:
- connectivityinstance
- destinationinstance
2. package.json
Here we declare dependencies to express (to provide a server with an endpoint) and axios (to execute requests)
"dependencies": {
"express": "^4.16.3",
"axios": "^0.18.0"
On command line, jump into the folder cloudToOnprem and execute npm install
3. server.js
Our implementation file for all the code. Of course, you would structure it in a better way, but for a blog, where I cannot upload project, it is better to keep the number of files to be shown in the appendix rather low
One basic thing we do in the beginning of our code is to access the environment variable of our app to get the credentials of the destination and connectivity service
This is possible because we’ve bound our app to these instances
const VCAP_SERVICES = JSON.parse(process.env.VCAP_SERVICES);
const destSrvCred = VCAP_SERVICES.destination[0].credentials;
const conSrvCred = VCAP_SERVICES.connectivity[0].credentials;
We need these credentials in order to call the service instances
4. Define app endpoint
We need an express server and an endpoint, such that we can interact with our app in the cloud
const express = require('express')
const app = express()
. . .
app.listen(process.env.PORT, function () {
. . .
app.get('/callonprem', async function (req, res) {
. . .
5. Call Destination Service
We want to call the destination service in order to access the destination configuration which we created in the preparation section above
We know that this step is optional, as we could as well hardcode the URL and the user credentials here in the code
But as mentioned, using the destination service is better
The destination service itself is protected with OAuth 2.0 so we have to do the OAuth flow before we can read the configured destination
So first we have to fetch the JWT token, which we do in a helper function
const destJwtToken = await _fetchJwtToken(destSrvCred.url, destSrvCred.clientid, destSrvCred.clientsecret)
In the implementation of the helper function, we do a simple GET request with axios, which we configure with the required info
const _fetchJwtToken = async function(oauthUrl, oauthClient, oauthSecret) {
const tokenUrl = oauthUrl + '/oauth/token?grant_type=client_credentials&response_type=token'
const config = {
headers: {
Authorization: "Basic " + Buffer.from(oauthClient + ':' + oauthSecret).toString("base64")
}
}
axios.get(tokenUrl, config)
. . .
URL
The URL is composed by the oauth endpoint which we’ve read from the environment variable
The appended parameters are specific to the OAuth flow (more info here)
The OAuth endpoint is located on the OAuth Authorization Server, which is provided by XSUAA
Authorization
We have to provide credentials (basic auth) to access that server
Luckily, we have these credentials in the environment variable
Once we have the JWT token, we can call the destination service
The URL of the service is handed over to us in the environment variable as well
Furthermore, the REST API of the destination service allows to ask for one specific destination. As such, we pass the destination name which we entered in the destination configuration
So we can create a helper function which we invoke as follows:
const destiConfi = await _readDestinationConfig('destination_s4hmock', destSrvCred.uri, destJwtToken)
In our helper function we use the JWT token for authorization on the destination service
The URL is composed using the env variable and according to the docu of the REST API
const _readDestinationConfig = async function(destinationName, destUri, jwtToken){
const destSrvUrl = destUri + '/destination-configuration/v1/destinations/' + destinationName
const config = {
headers: {
Authorization: 'Bearer ' + jwtToken
}
}
axios.get(destSrvUrl, config)
The result of the call is a JSON object carrying all the configuration data which we entered when we created the destination configuration above
And yes, we get the password of the technical user in plain text
But you don’t need to panic, it is the same password which I wrote above
6. Call Connectivity service
Now that we know that URL that we want to call (we knew it anyways, we could have saved lot of “reading minutes” for this blog…), we can go ahead
But as mentioned before we cannot just call a “virtual” URL like
http://s4mock:3000/products
This service-URL is served by the Cloud Connector along with a proxy server
The URL of the proxy server is provided by the connectivity service
So let’s retrieve it
Again, to call the connectivity service, we have to authenticate with OAuth 2.0
Again, to fetch the token, we get the required credentials from the environment variable of our deployed and bound application
So we can use our helper function in the same way like above
const connJwtToken = await _fetchJwtToken(conSrvCred.token_service_url, conSrvCred.clientid, conSrvCred.clientsecret)
And then we use the token to call the proxy server
The URL of the proxy server is taken from the env
connSrvCreds.onpremise_proxy_host
connSrvCreds.onpremise_proxy_http_port
The internal proxy looks like this in my example:
Here’s an example of how a proxy server looks in my case:
connectivityproxy.internal.cf.eu10.hana.ondemand.com
The authorization is done with the JWT token, as mentioned, but different than before:
There’s a special header required to authenticate at the proxy:
headers: {
'Proxy-Authorization': 'Bearer ' + connJwtToken,
The actual target URL which we call, is our virtual host, as retrieved from the destination, concatenated with the path to the endpoint, as defined in our silly mock back-end
Another interesting point: since our Cloud Connector is identified via LocationID, we have to specify that somewhere.
This is the reason why we specified a LocaltionID at all: to be able to show how it is specified.
And here comes the solution:
It is another header: ‘SAP-Connectivity-SCC-Location_ID’
And we have the value in the destination configuration, we don’t have to copy&paste
It looks like this:
headers: {
'SAP-Connectivity-SCC-Location_ID': destiConfi.CloudConnectorLocationId
That’s all we need
We have obviously a helper function to do the work
const result = await _callOnPrem(conSrvCred.onpremise_proxy_host, conSrvCred.onpremise_proxy_http_port, connJwtToken, destiConfi)
and the work is done like this:
const _callOnPrem = async function(connProxyHost, connProxyPort, connJwtToken, destiConfi){
const targetUrl = destiConfi.URL + '/products'
const encodedUser = Buffer.from(destiConfi.User + ':' + destiConfi.Password).toString("base64")
const config = {
headers: {
Authorization: "Basic " + encodedUser,
'Proxy-Authorization': 'Bearer ' + connJwtToken,
'SAP-Connectivity-SCC-Location_ID': destiConfi.CloudConnectorLocationId
},
proxy: {
host: connProxyHost,
port: connProxyPort
}
}
axios.get(targetUrl, config)
The result of this call is the response of our back-end-mock
We can do with it what we want.
We want nothing
So we just pass it as response or our cloud app
Please forgive me that there’s no error handling etc
(My apologies aren’t meant totally sincere, the blog is already too long, so skipping as many lines as possible)
Step 3: Deploy and Run
Finally we can push our app to the cloud
Once it is successfully deployed, we can invoke our test-endpoint.
In my example, the URL is as follows:
https://onpremcaller.cfapps.eu10.hana.ondemand.com/callonprem
And the result is the response of our local back-end mock server:
We can see:
Our app in the cloud has gone through the Cloud Connector and fetched the data exposed by the mock server on our local laptop
Cool – isn’t it?
Step 4: Undeploy and Sleep
We’ve reached the end of our tutorial (after 18 minutes of read…)
We can now clean the cloud and remove our dirty onPrem-Test-application
cf d onpremcaller
We can also stop our mock server
And we can shutdown our Cloud Connector
And we can put our laptop in sleep mode
Good bye … ?
Summarize
Instead of saving some lines and some seconds of read, I have to add this summary, because it is best practice, etc
So, as all of you noticed, we’ve managed to call onPrem system from the cloud
We’ve deployed an app to the cloud and this app was able to call an endpoint on our local laptop
That’s a bit surprising and it was made possible with Cloud Connector and Connectivity Service
Furthermore, we used the Destination Service (instead of saving some minutes of read)
The real scenario with details:
Quick Guide
The essential takeaway of this tutorial, from my perspective:
To call onPrem from Cloud Foundry, we need to call destination service and get proxy info
Then call the virtual URL via proxy:
const config = {
headers: {
Authorization: "Basic " + encodedUser,
'Proxy-Authorization': 'Bearer ' + connJwtToken,
'SAP-Connectivity-SCC-Location_ID': destiConfi.CloudConnectorLocationId
},
proxy: {
host: connProxyHost,
port: connProxyPort
}
}
axios.get(targetUrl, config)
Links
- SAP Cloud Connector documentation
- Connectivity documentation
- axios in npm and github
Appendix 0: Folder Structure
Root project and working directories:
Appendix 1: Sample Code of Mock Back-end
package.json
{
"dependencies": {
"express": "^4.16.3"
}
}
server.js
const express = require('express');
const app = express()
app.get('/products', function(req, res){
const auth = req.headers.authorization || ''
const user = Buffer.from(auth.substring(6), "base64").toString('utf8')
console.log("User: " + user)
if(user === 'john:secret'){ // simulating basic auth
res.json([{Id: "HT-1000", name: "Broken Laptop"}]);
}else{
res.status(401).send('Forbidden: invalid user or password');
}
});
app.listen(8080, () => {
console.log('server running')
})
Appendix 2: Sample Code of Node.js App
manifest.yml
---
applications:
- name: onpremcaller
command: node server.js
memory: 128M
buildpacks:
- nodejs_buildpack
services:
- connectivityinstance
- destinationinstance
package.json
{
"dependencies": {
"express": "^4.16.3",
"axios": "^0.18.0"
}
}
server.js
const axios = require('axios')
const express = require('express')
const app = express()
const VCAP_SERVICES = JSON.parse(process.env.VCAP_SERVICES);
const destSrvCred = VCAP_SERVICES.destination[0].credentials;
const conSrvCred = VCAP_SERVICES.connectivity[0].credentials;
app.listen(process.env.PORT, function () {
console.log('CloudToOnprem application started')
})
app.get('/callonprem', async function (req, res) {
// call destination service
const destJwtToken = await _fetchJwtToken(destSrvCred.url, destSrvCred.clientid, destSrvCred.clientsecret)
const destiConfi = await _readDestinationConfig('destination_s4hmock', destSrvCred.uri, destJwtToken)
// call onPrem system via connectivity service and Cloud Connector
const connJwtToken = await _fetchJwtToken(conSrvCred.token_service_url, conSrvCred.clientid, conSrvCred.clientsecret)
const result = await _callOnPrem(conSrvCred.onpremise_proxy_host, conSrvCred.onpremise_proxy_http_port, connJwtToken, destiConfi)
res.json(result)
})
const _fetchJwtToken = async function(oauthUrl, oauthClient, oauthSecret) {
return new Promise ((resolve, reject) => {
const tokenUrl = oauthUrl + '/oauth/token?grant_type=client_credentials&response_type=token'
const config = {
headers: {
Authorization: "Basic " + Buffer.from(oauthClient + ':' + oauthSecret).toString("base64")
}
}
axios.get(tokenUrl, config)
.then(response => {
resolve(response.data.access_token)
})
.catch(error => {
reject(error)
})
})
}
// Call Destination Service. Result will be an object with Destination Configuration info
const _readDestinationConfig = async function(destinationName, destUri, jwtToken){
return new Promise((resolve, reject) => {
const destSrvUrl = destUri + '/destination-configuration/v1/destinations/' + destinationName
const config = {
headers: {
Authorization: 'Bearer ' + jwtToken
}
}
axios.get(destSrvUrl, config)
.then(response => {
resolve(response.data.destinationConfiguration)
})
.catch(error => {
reject(error)
})
})
}
const _callOnPrem = async function(connProxyHost, connProxyPort, connJwtToken, destiConfi){
return new Promise((resolve, reject) => {
const targetUrl = destiConfi.URL + '/products'
const encodedUser = Buffer.from(destiConfi.User + ':' + destiConfi.Password).toString("base64")
const config = {
headers: {
Authorization: "Basic " + encodedUser,
'Proxy-Authorization': 'Bearer ' + connJwtToken,
'SAP-Connectivity-SCC-Location_ID': destiConfi.CloudConnectorLocationId
},
proxy: {
host: connProxyHost,
port: connProxyPort
}
}
axios.get(targetUrl, config)
.then(response => {
resolve(response.data)
})
.catch(error => {
reject(error)
})
})
}
Appendix 3: Screenshots of Cloud Connector
- Project structure and working directory for unzipped Cloud Connector:
- Start up of Cloud Connector: up and running:
- Plugging the Cloud Connector to my subaccount
- Cloud Cockpit: Subaccount: view our Cloud Connector appearing in the cloud
- Plugging the Cloud Connector to my local mock server (representing the onPrem system)
- Check the connection
- Second configuration step for onPrem connection
Well explained Carlos .. by looking at this anything can be achieved.
Hi Yogananda Muthaiah thanks soo much for your feedback and for taking your time !! That's motivating!
Cheers, Carlos
Hi Carlos,
I get the following error when I try your example : Error: tunneling socket could not be established, cause=connect ECONNREFUSED 127.0.0.1:20003
Any idea why?
EDIT: Sorry it works.
Doesn’t work in local development – at this point you can check if node is running in production (CF NODE_ENV=production) then only then add the Proxy to the request config else use request config without proxy and it will work. (You need to be on Corporate Network or VPN)
Also for AXIOS when doing local development if the resource you are trying to reach might throw SSL error at that point you can use the following in the axios config.
httpsAgent: new https.Agent({
rejectUnauthorized: false
})
I handle it currently the following way in my function.
Hi Abhijith Ravindra ,
In the name of all readers, thanks VERY much for this useful comment !!
Cheers, Carlos
Hi Carlos,
congrats again on the great blog and thanks to everyone who also appended to it in the comments. I am struggling with enabling this scenario for PrincipalPropagation.
Currently it is setup for basic authentication and this works for sure, but actually the new approach that SAP recommends for backend connectivity is somehow not working in my case.
Here is what i did different than the example above -
According to the documentation, this token should be the user exchange access token, but i am not sure if this is really it, how can i retrieve such a token using node.js? There are examples for JAVA, but i couldn't find one for node.js.
Also the cloud connector doesn't generate the temporary certificate, because it comes back with the following error -> No principal found for connection with ....
So this means to me that somehow the cloud connector does get the information as to who is trying to access the respective service.
I saw some more blogs from you Carlos and other experts on using an additional destination to retrieve the user exchange token, but is this mandatory or is this just another way of doing the same thing, like explained in this blog?
Help is appreciated.
BR,
Todor
Hello Todor Petrov ,
thanks so much for your nice feedback!
Unfortunately, I'm not expert for principal propagation ;(
I can only explain one thing: token exchange
Token exchange is used in the following case/scenario
you have an app and your app uses another service
both your app and the service are protected
both require a scope, but different scope
As such, when the end user calls your app, the end-user needs to have the scope to call your app (scope is provided to the user via role/role-collection)
For design reason, the scope of the needed service is not provided to the user via role (service-scope is internal and not mean to be visible to end-users)
As such, your app cannot just forward the token (received from the user) to the service
But since there IS a valid token, it can be used to obtain a new token, with the scope of the service
As such, you can do token-exchange: send a valid token -> and receive a valid token with the new scope
Hope this helps a bit.
Cheers,
Carlos
Hi Carl,
Thanks for the great blog, I have tried the same steps and I am facing 2 issues.
Failure reason: "Backend is not available in the list of defined system mappings in Cloud connector"
I can see the "mylocalmachine" available in cloud connectors and connected also s4hmock:3000 expose as backend.
2) When I push the app using VSCode I am facing below error
app can't be mapped to https://onpremcaller.cfapps.eu10.hana.ondemand.com/callonprem because the route exists in different space.
Thanks
Hi Carl,
I found the issues, redoing mock server destination in subaccount helps to fix the connection issues.
Renamed few service to unique names than copying the same like in blog.
Thanks
Rajesh
Hi rajesh kelam , Thanks very much for posting your solutions, that's helpful for other users !
GREAT
HI Carlos,
Thanks for the blog. I have followed the same steps as mentioned in the blog. I have used the mock server instead of proper backend system.
I am facing the below error while accessing endpoint of mine.
Any idea?
Thanks in advance..
Hi Ravikiran Kongati ,
in my case, whenever I get that 502 error, the reason is simple: the app which I've deployed has crashed and didn't start.
The route in CF still exists, therefore the Bad Gateway error.
I guess you should check the CF logs
Kind Regards,
Carlos
AppDeployedstate
Thanks for quick response.
Interestingly, App has started for me and it is in Green color.
when i have checked the logs, i see messages as attached.
Regards,
ravi
Hi Carlos,
It was temporary issue with my BTP account.
It started working today..
Regards,
Ravi
Good to know, thanks
Carlos Roggan
Good to see your blogs!!!! i still remember you helping me to figure out few issue in SMP Way back in 2015. Great blog!! keep it up... and thanks for writing these kinda of blogs.
Hi Vishnu Pankajakshan Panicker ,
And good to see YOU still here !!! 😉
Wow, that was loong long time agoooo ;!
Thanks so much fo ryour comment and wish you all the best !
Cheers,
Carlos 😉
Dear Carlos Roggan
thanks for your explanation.
I would like to do the same via a service to application router scenario by configuring just the xs-app to access what I need.
I tried it and as explained in this question it seems like there is an issue in the reachable of the destination service (maybe in the token exchange flow). It works just with the environment destination level, not with the instance/subaccount ones.
Did you had the chance to try the same?
Thanks in advance,
Rossano
Hello Rossano Rosica ,
unfortunately, I'm not aware of that scenario, also not sure how to solve your question.
I've tried something like an answer there, maybe it helps a little bit.
Kind Regards,
Carlos
Carlos Roggan,
Thanks for the blog, it really helped understanding the topic of calling onprem system.
However, the solution does not work on my end. I have an existing on prem system which I can access via a VPN. I created a destination with my username in it and when I try to call the system I get a 401 all the time.
I can only access `sap/public/ping` endpoint, which is only a ping returning a html with "Server reached" body.
This appears to be a user rights problem, but I can access the odata services with my user, not really sure where the problem lies.
Thanks and all the best!
Hi Adrian,
these are very tedious problems and very hard to troubleshoot, I fear.
When calling onPrem system, you need a destination with configured cloud connector.
You then call the URL which contains the virtual host.
The real odata service URL is not configured in the destination.
When using the destination to call your endpoint, the call goes through cloud connector.
There, you have logs.
Maybe this helps you to find where the problem is located.
I'm sorry, I'm not so familiar here
BTW, the "check connection" button in the cockpit is not reliable, as it does only a ping to the destination service, afaik.
Kind Regards,
Carlos
Thank you for this detailed article.
How can we achieve calling an sap client other than the default one (100) in the same system ?
Because I think, adding the 100 in the destination config does not actually do anything to our call.
I tried sending the header "sap-client : 300" with no luck.
Was anyone able to achieve this using this method ?