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, Happy New Year 2024 🎆

This is the continuation of my first blog. I strongly recommend reading this before you jump onto this.

https://blogs.sap.com/2023/12/16/session-handling-on-cpi-part-1/

Methodology used:


As I mentioned at the end of my previous blog, we want to use two different users for services, but if we go with a message exchange/integration flow, I told the disadvantages.

Change the session handling to None😐. Then how?

  1. Log in with the first credential. We will get the response in the header Set-Cookie.

  2. Store the cookie in the global variable using the write variable option.

  3. Before calling the service, explicitly add the cookie header via the content modifier.

  4. Repeat the same strategy for another user.


So if the old cookie expires, a new session generated will overwrite the variable with new cookie details. Storing the cookie/session in a plain format over a variable is not a good practice.

S, I added one extra step with the help of hashing on nodejs and secure parameters on CPI.

I am generating hash on the node side with

  • Message -> session ID + user_secret( secure parameter stored in CPI and transferred via custom header )


The resultant hash will be stored on the session store against the session ID. So each time with a cookie they have to pass this secret. We will derive the hash again. If the derived hash and hash in the session match then only the session is valid, otherwise we can invalidate the session.

So if someone accesses the cookie, they are unable to use it until they get this secure parameter.

(i.e.: Hashing is also the costliest function in terms of execution, Maybe if there is a better secure way to store the cookie on the CPI side, please tell on comments ).

Steps:


Two global variables for storing the respective cookie

  1. session_normal

  2. session_admin


user_secret - Passed on customer header and used on hashing at server side.

  1. Get the user_secret  and transfer via custom header to the node.

  2. Check if general_user_cookie is available on the variable store. If available then set it onto the request header cookie and call the login endpoint.

  3. If the cookie is valid, the server performs the hashing and compares the hash. In case of the match it returns the status that the session was authorized else returns an error.

  4. If the cookie is expired/invalid/not passed, the server tries to authenticate and return the session in the Set-Cookie response header.

  5. If the 4th point has happened, then update the variable session_normal with a new one and modify the request header also.

  6. Call the employee endpoint.

  7. Repeat the same thing for the admin user with appropriate changes on settings.


( Attaching All images in last section  related to iflow)

Node JS code:


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

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

//URL PARSING Middleware
expressApp.use(parser.urlencoded({
extended: true
}))
// New Mapping for storing Hash
let session_hash_map = new Map();

//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) {
const message = req.session.id + req.headers.secretid // Session ID + secret coming from CPI
// console.log("Message to Hash:",message)
if (req.session.loggedin || req.session.loggedinadmin) { // IF already loggedin
console.log(req.session.id)
if (req.session.loggedin) {
console.log("Session Founded Normal User")
} else {
console.log("Session Founded Admin User")
}
// COmpares with original hash that stored during first time login
bcrypt.compare(message, session_hash_map.get(req.session.userid), function (err, result) {
console.log(result)
if (result) {
res.set('custom_status', 'A')

res.status(200).send({
"message": "Session Authorized succesfully"
})
}
//If hash not matches
else {
session_hash_map.delete(req.session.userid)
req.session.destroy();
res.status(401).send({
"message": "Session invalidated . Please Login Again"
})

}

});
} 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 = user // user id property
req.session.loggedin = true; // Logged In Normal
bcrypt.genSalt(10, function (err, salt, res) {
// Hash and store it on map 🧑‍💻
console.log("New Login Normal")

bcrypt.hash(message, salt, function (err, hash) {
console.log("Derived hash Normal", hash) // Printing Hash
session_hash_map.set(user, hash) // Setting Hash

});

})
res.set('custom_status', 'N')
res.status(200).send({
"message": "Logged In succesfully and session set for 1 hour"
})

} else if (user == "SAPCPI_ADMIN" && pass == '98765') {
req.session.userid = user
req.session.loggedinadmin = true; // Logged in Admin
bcrypt.genSalt(10, function (err, salt, res) {
// Hash and store it on map 🧑‍💻
bcrypt.hash(message, salt, function (err, hash) {
console.log("New Login Admin")

console.log("Derived hash Admin", hash) // Printing Hash
session_hash_map.set(user, hash) // Setting Hash


});

})
res.set('custom_status', 'N')

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) {
if (req.session.loggedin) {
console.log("Giving Employee Details")
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) {
if (req.session.loggedin) {
console.log("Giving Customer Details")

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) {
console.log("Giving Payment Details")

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")
});

 

Iflow Design :



Iflow-1



Iflow-2


 


Setting secret in a custom header



Set Cookie For General User



Login Call-General User



Route



Write a Cookie in the variable



Set cookie in the header



Endpoint Call



Overwrite cookie


 

The process will be repeated for another set.


Cookie in Variable


 


1st call



Further, call until the session expires


 


output



 

Summary


In these two parts, I explained the session handling mechanism and how actually it works with the help of Node JS.

As I said this requirement is all self-created for this blog purpose, I hope it will give better/deep insight into session handling with nooks and corners which will be used in realtime scenarios.

If you have any queries/suggestions ( over CPI / Node JS ) feel free to comment.

Thanks...
2 Comments
Labels in this area