-
Can anyone please offer some suggestions while using MultiSamlStrategy? I am using "@node-saml/passport-saml"^5.0.0 with Node 16.29. Previously, I was using a SAML strategy that simulated multi by creating a new instance of SAMLStrategy for each login request for different IdPs. I thought this introduced a session leak since it sometimes appeared to be holding onto another SAML instance when two users logged in simultaneously. I was able to reproduce this many times by testing in Chrome and Edge simultaneously. I then switched to MultiSamlStrategy to see if this alleviated the problem - see sample code below. Background:
I use express-session with MongoDB for my session store and do the necessary Passport init:
I understand when using SSO there are actually two calls to passport.authenticate('saml'...):
The problem I sometimes run into: The first passport.authenticate() hits my passport.use() logic, and I always have access to the req.session.user object. Using that object, I can get the SAML credentials - good. The second passport.authenticate fires off after the IdP calls back (login/callback). When the passport.use() middleware gets hit the second time, sometimes the req.session.user object is not there. This is causing intermittent failures. It happens about 10% of the time. The madness here is that it happens sometimes, so it's been a debugging nightmare. I have checked my session logic many times over (FYI: I've also been using passport-local strategy for 5+ years, although there is only one auth call for this strategy). I'd like to know if anyone else has possibly experienced something similar? Or if anyone has time to look through my sample code below and tell me if they see anything glaring (or obvious!) that I'm missing. I included snippets from app.js, and authtypes,js, with most of the logic in users.js
|
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 2 replies
-
It would be easier to take a quick look into your example code if it would be properly indented and syntax highlighted (about syntax highlighting see https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting ). For anyone who try to solve the puzzle more background is probably available at node-saml/node-saml#379 tl;dr; background for aforementioned issue seems like stress testing with chaos testing twist due to not terminating SSO at IdP (only local logout) and using same browser instances to trigger new logins to SP (IdP might be seeing still alive IdP related login session and act accordingly).
Out of curiosity: Are you referring to this node-saml/node-saml#379 or are you you saying that passport-saml configurations/instances of passport-saml strategies were somehow mixed.
From SAML/passport-saml point and terminology point of view first call triggers SP initiated login (in case of multisaml strategy saml config is setup per request...this sample code has named saml config as credentials) with IdP and ACS endpoint (second call) receives result of login. In case of successfull login result contains assertion which contains information released by IdP for this SP about successfully autenticated user. Passport-saml validates digital signature(s) and few other things before passing information from assertion to application layer code. "Validation against logged in username" is something that this particular application does at application layer and is outside of "SAML scope".
Are you talking about your IMHO it is obvious why your getSAMLOptions can always access req.session.user
So now we are talking about this Based on information from your previous issue (stress testing) I wonder whether you have IdPs at different domains (TLDs or at subdomains of tlds which are blacklisted from cookie sharing point of view) than your application's domain? If so could it be possible that about 10% of your stress test logins happen at such IdP and if thats the case modern browsers do not send cookies when such IdP forwards user's browser back to your application via HTTP POST binding. Since there aren't any express session cookies available req.session.user is not filled by session management layer when POST to /login/callback arrives. You should use some other information from req to determine proper saml config (i.e. do not rely on information that requires cookies). |
Beta Was this translation helpful? Give feedback.
-
Appreciate these additional tips and will try them out. Quick note regarding SLO - I have pushed for this but the vendors we support specifically stated they don't want it. I tried to sneak it in and they complained, "why do we have to do all the login steps again." :-\ I even went so far as to add a checkbox option in our authtype editor reading, "Full Logout (SLO)" which defaults to false. Just in case they change their mind we can easily flip the state and the code is already set up to proceed w/SLO after the app logout. Thanks, again. |
Beta Was this translation helpful? Give feedback.
-
Great tip regarding RelayState as this helped me in the cases where the req.session was no longer holding my user object. It appears to reliably pass back my data from Idp SAML request. // login
let username = req?.session?.user?.username
console.log(`req username before passport.auth :: ${username}`);
passport.authenticate('saml', {
additionalParams: {RelayState: username},
failureRedirect: '/users/login',
failureFlash: 'Error during SAML authentication'
})(req, res, next);
//Process SAML request
passport.use(new MultiSamlStrategy(
{
passReqToCallback: true,
getSamlOptions: async (req, done) =>
{
try
{
let sessUsername = req?.session?.user?.username;
let relayState = req?.body?.RelayState;
const username = sessUsername || relayState;
console.log(`>>Username: ${username}`);
if (!username)
{
let errorMessage = "Error processing your SSO username.<br/>Please verify and try again.";
return done(null, false, {message: errorMessage}); // Pass the error message through
}
const samlCredentials = await authtypes.getSAMLCredentials(username); |
Beta Was this translation helpful? Give feedback.
If you need to pass state you can use SAML's RelayState for that (instead of cookie). Any string (I dont remember max length) is passed as-is over login sequence back to SP. String could be e.g. reference to user.
BUT due to your policy of hating/avoiding SLO even on this stress test situation (and not even trying to setup / cleanup things like browser state to resemble normal - you wrote at previous issue that normal is single user per browser instance) combined with possible usage of RelayState opens up corner case possibilities. I don't feel like starting to write down speculations just because you dont want to properly teardown/initialize test rounds...just think of situations when yo…