Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
Nagaraj
Explorer

Introduction:


Hello all, In this blog series, I will explain session handling on CPI with the help of an HTTP server running on NodeJS on my personal laptop (i.e., wherever on-premise on this blog, it is referred to my personal system). Cloud Connector is used for connecting the CPI trial tenant with my node server.

Simple Flow Diagram



Prerequisites:


1. CPI System ( I used a trial account )

2. Cloud Connector Installed on your on-premise.

3. NodeJS HTTP server running on my on-premise.( with session handling )

4. Basic knowledge of CPI

Disclaimer / Points to Note:


1. I will not explain NodeJS code for the most part since it will deviate the blog from its original direction. I added a self-explanatory comment. If you have any query on same, ping on comment.

2. The requirement used for this is purely created for this blog's purpose. So at a certain point, you may feel the requirement is unrealistic. My objective is to explain the functionality of session handling.

What is an HTTP Session?


Skip the part if you already know.

As you know, HTTP is stateless. This means it will not maintain any state between the server and the client. Each server request will be treated as a new request.

Eg:, if an online shopping website asks you to log in for each page visit or each action that you perform, imagine an HTTP stateless website like this. But we are not doing this; how? Because with the help of things like sessions (i.e., there are also more methods).

The session will help to make HTTP look stateful. The session will be server-side storage.

When you first authenticate on the server, it will issue a session ID (a long random string) to you. The server will store your details in the backend along with a long random string. So on further subsequent requests, you will attach that session ID as a cookie; if it was a valid session, the server grants you access.

Simple Flow Diagram




Supported Adapters:


You can see the supported adapters on this blog.

https://blogs.sap.com/2017/07/17/cloud-integration-how-to-configure-session-handling-in-integration-...

we are using an HTTP receiver adapter.

Cloud Connector Configuration:



There is one more thing there. If you see http://locahost:8086 configured to two virtual hosts. One I settled during my first time preparation and the second was done for the blog actually.

If you see the domain on my below node js code, you can see localhost set up there on the session code. so if you are mapping the same internal host ( with the same or different port ) to more than one virtual host, the virtual domain in the cookie will be set wrongly which will not lead to picking the cookie on the CPI side.

So, I added a cookie domain like this.


without this ( Wrong Pick of Virtual domain on the cookie)


with this



Node JS setup:



  1. /login route - Used for logging in.

  2. /getemployeedetails   - Used to get employee details ( User: SAPCPI )

  3. /getcustomerdetails    - Used to get customer details  ( User: SAPCPI )

  4. /getpaymentdetails     - Used to get payment details   ( User: SAPCPI_ADMIN )


const express = require('express')
var parser = require('body-parser')
var session = require('express-session')

const expressApp = express(); // Created Express App

//URL PARSING Middleware
expressApp.use(parser.urlencoded({
extended: true
}))
//JSON PARSING Middleware
expressApp.use(parser.json());
// SESSION Middleware
expressApp.use(session({
secret: 'dsufhodsfhdiajdbadkjbaoodhdjbfadljqslvsoif', // Usually a long secret maintained in environment. For testing I maintained here
cookie: {
httpOnly: true, // Prevent reading the cookie from other than HTTP
maxAge: 150000, // Validity of session on milliseconds
domain: 'localhost' // Domain for cookie
},
resave: true,
saveUninitialized: false,
name: 'Session_ID' // Name on session id cookie on response
}))
//Login Route
expressApp.get("/login", function (req, res, next) {
console.log(req.session.id)
if (req.session.loggedin) { // IF already loggedin
res.status(200).send({
"message": "Session Authorized succesfully"
})
}
else {
const auth = new Buffer.from(req.headers.authorization.split(' ')[1], 'base64').toString().split(':'); // Getting user name and password from auth header
const user = auth[0];
const pass = auth[1];
if (user == "SAPCPI" && pass == '12345') { // For simplicity purpose
req.session.userid = req.body.userid // user id property
req.session.loggedin = true; // Logged In
res.status(200).send({
"message": "Logged In succesfully and session set for 1 hour"
})
}
else if (user == "SAPCPI_ADMIN" && pass == '98765') {
req.session.userid = req.body.userid
req.session.loggedin = true;
req.session.loggedinadmin = true;
res.status(200).send({
"message": "Logged In succesfully and session set for 1 hour"
})
}
else {
res.status(401).send({
"message": "Invalid Credentials. Please try again"
})
}
}
});
// Employee Details
expressApp.get('/getemployeedetails', function (req, res) {
console.log(req.headers.cookie)
if (req.session.loggedin) {
res.status(200).send({
"meessage": "Here you can found employee details"
})
} else {
res.status(401).send({
"message": "Unauthorized. Please login"
})
}
})
// Customer Details
expressApp.get('/getcustomerdetails', function (req, res) {
console.log(req.headers.cookie)
if (req.session.loggedin) {
res.status(200).send({
"meessage": "Here you can found vendor details"
})
} else {
res.status(401).send({
"message": "Unauthorized. Please login"
})
}
})
// Payment details
expressApp.get('/getpaymentdetails', function (req, res) {
if (req.session.loggedinadmin && req.session.loggedin) {
res.status(200).send({
"meessage": "Here you can found payment details"
})
} else {
if (req.session.loggedin) {
res.status(401).send({
"message": "You have no access to payment data.Contact Admin"
})

} else {
res.status(401).send({
"message": "Unauthorized. Please login"
})

}
}
})
expressApp.listen(8086, function () {
console.log("Server Listening on http://localhost:8086")
});

Types of Session Handling:


There are 3 types

1. None - Session will be not reused.

2. On Exchange - The Session will be valid under one message.

3. On Integration Flow - Session will be valid under the whole i-flow.


 

None:

As the name suggests, the session will not be reused. It was the default option.

I called the login route which actually initialize the session and return the session as a cookie. We need to send that cookie on further request to use the session. I triggered the endpoint from Postman.



The result is expected as 401 since we selected the HTTP session Reuse as none. Even though on the node side session was initialized, on the CPI side it was not reused on further calls.



 

On Message Exchange:

You guessed we have to change on Message Exchange. That will solve the problem. Yes but not only that.


 

Deployed and ended up with the same error. Now recall the definition, valid for one message exchange. Meaning the highlighted area was its validity.

 


How do we make it as one message exchange?

Make it a local process call like this.



 

Not only process calls, looping calls, and multicast we can use this.

Let's test this and now output came.



But is this really using a session? will check that by again calling the endpoint. If you see the new session ID was generated. It was not my expected behaviour. On further subsequent calls, we have to use the same session until it expires, for that.


On Integration Flow:

changed it to on Integration flow.


On further subsequent calls, if you see, the same session was used.

-----X-----


Now consider the scenario: you need to consume two services with a normal user and one service with an admin user. (Though admin has access to normal user services as well, imagine there is a restriction like admin can access only their related services.) So now it is decoupled.

We can go in two ways.


1. If we go with "on Message Exchange," create a two-local process and call each separately. But as discussed, it will generate a new session on each hit.


2. If we go with "on Integration Flow," you can maintain a session for one user; if we pass that cookie to another, it will throw an error. We had to log out of this session and log in again for another service, which was not proper usage of a session.


How do we handle this? Let's share this in the second part of this blog.


-----

Edited on : 09-01-2024

Part 2 Published : https://blogs.sap.com/2024/01/07/session-handling-on-cpi-part-2/

------

Sincerely appreciate any feedback/comments/questions/clarity feel free to comment.

Thanks
3 Comments
Wes_Ancog
Explorer
Nice. Very informative.
Nagaraj
Explorer
0 Kudos
Thanks
00022111734
Participant
0 Kudos
Hi Nagaraj

 

Thanks for sharing insightful Blog very well Explained .Appreciated
Labels in this area