Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
munishsuri
Participant
Use Case - Need to propagate technical user to the on premise S4 system using methodology of principal propagation. Authenticated applications running on BTP Cloud Foundry, can now propagate technical user utilizing SAP-Connectivity-Technical-Authentication header.

We recently had a requirement where we would need to propagate the Technical user but did not want to use Basic Auth as a security.

As of Cloud Connector version 2.15, consumers of the Connectivity service can propagate technical users from the cloud application towards the on-premise systems. To achieve this a JWT token is used representing the technical user, via the SAP-Connectivity-Technical-Authentication header.This is similar to principal propagation, but in this case, a technical user is propagated instead of a business user.

In the following blog, I would try to outline an approach for achieve the same, though it is something we have used to achieve the results and may not be production ready (Of Course), because the code should be more refined while achieving the same 🙂

 

 


 

To verify and demonstrate the functionality, where an OData service is used on premise that I fetching the current user in the call and returning to the caller. From Cloud Foundry perspective there is an application that will be serving as an entry point for the User and the call is forwarded to another middleware, where the headers will be transformed for the request.

Pre Req - Principal propagation is already setup.

Let's start with the configuration on the on premise side.

  1. Create an odata service - in this specific case to test the connection, a sample odata service is created, that is going to read the logged in user and return to the caller. Attaching a sample implementation - utilizing the current DDIC structures for the same and using sy-uname.

  2.   Creation of a User in Su01

  3.  In the above step the most important part is the alias -  this is the technical user for your         communication and you can find it from the BTP Service Key, in the above case this is the service key client id of the connectivity service of the sub account where you have connected your cloud connector. We are getting via binding the middleware application with connectivity service and using the environment variables to read the data.

  4. Now you would need to map the user via transaction certrule, as the technical user is in managed in alias, we would mapping the user via alias, and the cloud connector will have Principal Propagation as a means of authentication.            



 

From the On premise perspective, we would be required to do only few additional configurations apart from the standard principal propagation setup.

 

Pre Req - Cloud Foundry Application that has XSUAA authentication and capable to call a destination.

In our POC, we have an app router, which is going to call the destination (middleware application).

XS-app.json of the app is configured in the following way,
{
"welcomeFile": "/index.html",
"authenticationMethod": "route",
"logout": {
"logoutEndpoint": "/do/logout"
},
"routes": [
{

"csrfProtection": false,
"source": "^/sap/opu/odata/sap/Z_MUNISH_SSO_SRV/(.*)$",
"target": "$1",
"destination": "middleware_sso"
},
{
"source": "^(.*)$",
"target": "$1",
"service": "html5-apps-repo-rt",
"authenticationType": "xsuaa"
}
]
}

it is pointing to middleware_sso, which is nothing but a destination created on BTP sub account for the middle ware processing, though this can be achieved by middleware chaining concept, but I am putting the middleware logic out for this POC.

Config of middleware_sso destination are shown below.


 

Till Now, we have configured the Principal Propagation and have an application available that is bounded to XSUAA and able to call the destination.

Coming to the Main part of now calling the Odata Service Destination, for that purpose we have a node js middle ware to append header and call the destination of the backend.

Start.js 
const express = require("express");
const app = express();

const SapCfAxios = require("sap-cf-axios").default;

const passport = require("passport");
const { JWTStrategy } = require("@sap/xssec");
const xsenv = require("@sap/xsenv");
const xml = require("xml");

passport.use(new JWTStrategy(xsenv.getServices({ uaa: { tag: "xsuaa" } }).uaa));
app.use(passport.initialize());
app.use(passport.authenticate("JWT", { session: false }));
var axiosSimple = require('axios');

const oVCAP_SERVICES = JSON.parse(process.env.VCAP_SERVICES);
const oConnectivityServiceCredentials =
oVCAP_SERVICES.connectivity[0].credentials;

const handleRequest = async (req, res) => {

const axios = SapCfAxios("Dest_s4_he4_sso");
var authorization = req.headers.authorization;

var params = new URLSearchParams();
params.append("client_id", oConnectivityServiceCredentials.clientid);
params.append("client_secret", oConnectivityServiceCredentials.clientsecret);
params.append("grant_type", "client_credentials");

var tokentechnical = ""
try{
var response1 = await axiosSimple({
method: "post",
url: oConnectivityServiceCredentials.token_service_url + "/oauth/token",
params: params,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
},
});

tokentechnical = response1.data.access_token
}catch(e){
console.log(e)
}

try{

console.log("auth",authorization)
const response = await axios({
method: "GET",
url: "/ZMUNISH001Set",
headers: {
"content-type": "application/json",
"SAP-Connectivity-Technical-Authentication":"Bearer "+tokentechnical
},

});
console.log(response.data);
res.set('Content-Type','application/json')
res.send(JSON.stringify(response.data));

}catch(e){
console.log(JSON.stringify(e))
res.send(JSON.stringify(e))
}

};

app.get("/", handleRequest);
const port = process.env.PORT || 3000;
app.listen(port, function () {
console.log("myapp listening on port " + port);
});

 

For deployment you would be needing a manifest file.
---
applications:
- name: myapp
routes:
- route: <your_host>.cfapps.eu10.hana.ondemand.com
path: myapp
memory: 128M
buildpack: nodejs_buildpack
services:
- name: conn_principalpropagation
- name: dest_principalpropagation
- name: principalpropagation-xsuaa-service

Github ref

 

In the above code we are utilizing destination Dest_s4_he4_sso, this has be created in your BTP destinations pointing to the On Premise Virtual Host.


 

Now it is time to test the flow, you can call the app router Url and it should point to the middleware, from where you should be able to connect to the on premise system via a technical user and if everything is fine, technical user should be returned in the response if configured properly.

In the current setup of min, you may view VBAK table attributes and technical user , I just utilized a standard table component, please don't get confused here.


Conclusion - setup of technical user propagation was done via a middleware application and aligning backend system to accept the certs as per the alias defined in the technical user configurations.

 
9 Comments
gregorw
Active Contributor
Hi Munish,

thank you for providing this example. Great that the NPM module sap-cf-axios from joachim.vanpraet  is so flexible. I would love to use this technical user connections from within CAP and also from the SAP Cloud SDK for Javascript. Maybe you can add you project requirement also to the issue that I've created: Support for SAP-Connectivity-Technical-Authentication header #2974. Hope that this speeds up the implementation.

Best Regards
Gregor
Ben
Participant
0 Kudos
Hello Munish

This is great, thanks for this blog entry!

Is there a difference in BTP or in SAP S4 between a normal User and a technical user? What benefits do you see in propagating a technical user with the header instead of a normal user?

Best regards!
Ben
gregorw
Active Contributor
Hi Ben,

the main benefit that I see is that with this approach the creation and managment of a Basic Authenctication Password for the technical user can be avoided. Also the same destination in BTP can be re-used for technical and normal users.

CU
Gregor
Ben
Participant
0 Kudos
Hi Gregor

If you are using SAP ID Service (or better IAS or other custom IdP) the technical user has to authenticate some way and as far as I see there is no option without basic authentication at the moment when using SAP IAS service. Or am I missing something here?

There is also another good blog about this topic: Managing technical users for BTP platform access | SAP Blogs

Maybe munish.suri can explain how the technical user is authenticated in his example in the first place?

Best regards,
Ben
munishsuri
Participant
0 Kudos
Thank you so much for your suggestion Gregor, I will try to add some info in the above mentioned Git Issue.

Best regards

Munish Suri
munishsuri
Participant
Hello Benjamin,

 

Thank you so for asking the question, as Gregor mentioned advantages of this approach is that you don't need to manage Username password in the destination and the same can be utilized for named users Propagation.

In our use case - Customer has an application running on SAP BTP and they want to communicate with the S4 system using a technical user, because they don't want their BTP users (External users) to be created in S4 system, currently the communication happens via a destination having username password, and with this approach you don't need it anymore.

In the current scenario, if you observe the technical user is generating a JWT token (
"SAP-Connectivity-Technical-Authentication"

), which is forwarded to the cloud connector, where it gets verified to be forwarded to the S4 system. and for generation of the token Oauth is utilized (Client ID & Secret) which we are getting from the service key.

you may refer to - https://help.sap.com/docs/CP_CONNECTIVITY/cca91383641e40ffbe03bdc78f00f681/70b8ef33812e486d8b745a0b4...

 

Best regards

Munish Suri
gregorw
Active Contributor
Hi Ben,

I think you have to distinguish two use cases:

  1. Authenticaiton with a technical user to consume an API provided by an applicaiton deployed to BTP. That is covered by piejanssens in his post Managing technical users for BTP platform access

  2. Backend connectivity for the BTP application itself for cases described by Munish.


Best Regards
Gregor
dyaryura
Active Participant
0 Kudos
Hi Gregor

Thanks from referencing this great blog from Idea Place.

Maybe I'm asking too much but I would expect an easier way to set the client id (i.e CN=sb-clone....) in the destination service itself and not to set at environment variables level. I think it would be easier to set up everything in the destination itself using a new authentication type, let's say "Technical User Propagation" that should appear in the dropdown for the destination itself. Is this or something similar planned?

 

Thanks

Diego

 

 
joachimvanpraet
Active Participant
0 Kudos
Hi munish.suri ,

Thanks for your blog, I didn't know about the "SAP-Connectivity-Technical-Authentication" header.
I added it in the sap-cf-axios library, but was not able to test it yet.

Starting from version 0.4.8 you should be able to use it.
If no authentication header is set and the destination type is onpremise, we will set the "SAP-Connectivity-Technical-Authentication" header.
const express = require("express");
const app = express();

const SapCfAxios = require("sap-cf-axios").default;

const passport = require("passport");
const { JWTStrategy } = require("@sap/xssec");
const xsenv = require("@sap/xsenv");

passport.use(new JWTStrategy(xsenv.getServices({ uaa: { tag: "xsuaa" } }).uaa));
app.use(passport.initialize());
app.use(passport.authenticate("JWT", { session: false }));
var axiosSimple = require('axios');

const handleRequest = async (req, res) => {

const axios = SapCfAxios("Dest_s4_he4_sso");
var authorization = req.headers.authorization;

try{

console.log("auth",authorization)
// as this is a request to an onpremise destination without an authorization header
// The SAP-Connectivity-Technical-Authentication header is set instead of the SAP-Connectivity-Authentication, principal propagation with the client-id
const response = await axios({
method: "GET",
url: "/ZMUNISH001Set",
headers: {
"content-type": "application/json"
},

});
console.log(response.data);

// as this is a request to an onpremise destination with an authorization header
// The SAP-Connectivity-Authentication header is set, principal propagation with the logged in user
const response2 = await axios({
method: "GET",
url: "/ZMUNISH001Set",
headers: {
authorization,
"content-type": "application/json"
},

});
console.log(response2.data);

res.json(response.data);

}catch(e){
console.log(JSON.stringify(e))
res.send(JSON.stringify(e))
}

};

app.get("/", handleRequest);
const port = process.env.PORT || 3000;
app.listen(port, function () {
console.log("myapp listening on port " + port);
});