Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IdP-initiated SLO #221

Closed
arneroen opened this issue Jul 3, 2017 · 16 comments
Closed

IdP-initiated SLO #221

arneroen opened this issue Jul 3, 2017 · 16 comments

Comments

@arneroen
Copy link

arneroen commented Jul 3, 2017

I'm struggling to make IdP-initiated SLO work. As per several examples, I have the following routes:

app.post('/logout/callback', function(req, res) {
        req.logout();
        res.redirect('/');
});
app.get("/logout", function(req, res) {
        if (!req.user) res.redirect("/");
        console.log("initiating logout", req.log, req.user);
        return passport._strategy("saml").logout(req, function(err, uri) {
            return res.redirect(uri);
        });
});

In addition, logoutCallbackUrl is defined as '/logout/callback'. This works fine for SP-initiated logout. Obvivously not for IdP-initiated SLO, though, as being contacted at /logout/callback will not prompt the server to reply with a logoutResponse. I've not found any examples for how to implement IdP-initiated SLO.

I've found methods in the code, that are generating the logoutResponse, which leads me to believe that IdP-initiated SLO is probably implemented. However, the beginning of the "chain of calls" to get to it is the "authenticate" functionality.

I'd greatly appreciate any help with getting this to work! Thanks!

@jonathanpdiaz
Copy link

hey @arneroen.
Have you found something else on this? I entered into the issues section to write exactly what you describe.
I'm getting the LogoutRequest from my Identity Provider (One Login), but the API of the strategy doesn't seem to handle it.
Any comment would be much appreciated.
Thanks!

@Brynjulf
Copy link

@arneroen, @jonathanpdiaz you should change your callback from a post to a get. That fixed the issue for me.

@jonathanpdiaz
Copy link

I forgot to answer my own question after I found the issue. The library works perfectly.
In my case, we implemented the Single Logout using HTTP Redirect.
What would this mean? It means that after I hit log out on any Service Provider [SP] (my app is one of them) passport-saml will generate a SAML Request XML that will log out this particular SP on the IdP and the browser will be redirected to the next SP that shares a session that needs to log out. To do this, the IdP will go to that particular SP and use the SLO endpoint.

One extra thing to consider is that the Identity Provider [IdP] will also hit the SLO endpoint to confirm the logout at the end. There, you can redirect your site to the login page.

Looks like this: (remember that is the browser who will be redirected to each site -one by one- that means that you have the information of the current session that is logged in each cookie that enables you to logout.)

sp-slo

Long story short, here is my current implementations that seem to work so far.

/**
 * Logout from SP to IdP.
 * We need a logged in user to log out.
 */
router.get('/logout', isAuthenticated(), samlLogout);

/**
 * IdP notifies logout.
 * Possible cases:
 * 1: same SP requested logout, IdP confirms logout.
 * 2: IdP request logout after receive logout request from SP that shared a session.
 * note: we can't use isAuth here, we need to redirect the browser.
 */
router.get(
  '/slo',
  (req, res, next) => {
    if(req.session.passport && req.session.passport.user) {
      req.user = req.session.passport.user;
      return next();
    } else {
      return res.redirect('/auth/saml');
    }
  },
  samlLogout
);

/**
 * Generate a Logout Request.
 * Redirect to this request URL.
 * note: SAML auth internally requires a req.user object with this 2 properties:
 *  '@Format': req.user.nameIDFormat,
 *  '#text': req.user.nameID
 */
function samlLogout(req, res) {
  try {
    const strategy = passport._strategy('saml');
    strategy.logout(req, function(error, requestUrl) {
      if(error) console.log(`Can't generate log out url: ${err}`);
      req.logOut();
      // passport-saml is not removing the session.
      delete req.session.passport;
      res.redirect(requestUrl);
    });
  } catch(err) {
    if(err) console.log(`Exception on URL: ${err}`);
    req.logOut();
    delete req.session.passport;
    res.redirect('/auth/saml');
  }
}

We use this API on a Single Page App. In our context, if we have the passport user, we are good to go.
So the isAuthenticated method only needs to check this.

import compose from 'composable-middleware';

/**
 * Attaches the user object to the request if authenticated
 * Otherwise returns 403
 */
export function isAuthenticated() {
  return compose().use(function(req, res, next) {
    if(req.session.passport && req.session.passport.user) {
      // after auth, we get a passport object.
      req.user = req.session.passport.user;
      return next();
    } else {
      return res.status(403).send({
        message: 'Session expired.',
      });
    }
  });
}

Hope it helps to someone else, it took me a little bit to figure it out.

@saurabh2608
Copy link

We have a situation where login will be initiated from IDP and callback will come to SP. Is there a way we can use passport-saml to authenticate the SAML response and set the user session?

@chveragad
Copy link

We too have a situation where login is IDP-initiated. Our current application utilizes redis to hold the user session. I have several questions and hopeful for an answer:

  1. From the sample I got https://github.com/gbraad/passport-saml-example, it is used with main node module passport. Is the main passport node module required to use for passport-saml? How would it work with redis then?
  2. Can passport-saml be used to validate, decrypt and digest the SAMLResponse from an IDP initiated callback?

@cjbarth
Copy link
Collaborator

cjbarth commented Oct 2, 2018

@sagarwalsf @chveragad , yes IdP initiated sign on should work just fine if your callback URLs are set up correctly; I've gotten it to work.

@umardraz
Copy link

@jonathanpdiaz

I tried your solution here is my code

router.get('/logout', isAuthenticated(), samlLogout);

router.get(
    '/slo',
    (req, res, next) => {
      if(req.session.passport && req.session.passport.user) {
        req.user = req.session.passport.user;
        return next();
      } else {
        return res.redirect('/auth/saml/login');
      }
    },
    samlLogout
);

function samlLogout(req, res) {
    try {
      const strategy = passport._strategy('saml');
      strategy.logout(req, function(error, requestUrl) {
        if(error) console.log(`Can't generate log out url: ${err}`);
        req.logOut();
        delete req.session.passport;
        res.redirect(requestUrl);
      });
    } catch(err) {
      if(err) console.log(`Exception on URL: ${err}`);
      req.logOut();
      delete req.session.passport;
      res.redirect('/auth/saml/login');
    }
  }

Now the problem is whenever I tried to logout from my Wordpres sites then on successful logout they always get nodejs site /auth/saml/login page rather then their own wordpress login page.

Would you please help me how I can fix this

mikkopiu added a commit to espoon-voltti/evaka that referenced this issue Apr 20, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue Apr 21, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue Apr 21, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue Apr 21, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue Apr 21, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue Apr 22, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue Apr 22, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue Apr 22, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue Apr 22, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue Apr 22, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue Apr 23, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue Apr 27, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue Apr 28, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue Apr 29, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue Apr 30, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue May 6, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue May 6, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue May 6, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue May 6, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue May 10, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
mikkopiu added a commit to espoon-voltti/evaka that referenced this issue May 11, 2021
- Many browsers are starting to disable 3rd party cookies by default (even SameSite None) which breaks Single Logout with passport-saml that currently only supports logging out with a cookie
    - Browser details: https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/ and https://blog.google/products/chrome/more-intuitive-privacy-and-security-controls-chrome/
    - passport-saml issues: node-saml/passport-saml#419 and node-saml/passport-saml#221
    - This is actually a real security issue as passport-saml will give a successful looking response from `passport.authenticate()` in the logout callback which results in IdPs showing "successfully logged out" messages to the user -- even though the user is still fully logged in!
    - This also causes an usability issue when the user tries to initate SLO from another application, fails to end the eVaka session, attempts a logout from eVaka and gets an ugly JSON error message as a response when APIGW attempts to make a LogoutRequest to the IdP that already ended the session -> will be fixed separately
- Other systems like Shibboleth get around the 3rd party cookie issue with Single Logout by not utilizing cookies at all for this but instead use the SAML nameID (and sessionIndex) properties presented in all SAML LogoutRequest messages
    - Source: https://wiki.shibboleth.net/confluence/display/DEV/IdP+SameSite+Testing#IdPSameSiteTesting-SameSiteandSingleLogout
- When logouts are always only done through SAML there's no need for the logout cookie itself but the idea is actually useful as an effective "secondary index" for Redis:
    - By storing a nameID + sessionIndex keyed item pointing to the session ID, we effectively create a second index that can be used with just the SAML LogoutRequest's nameID and sessionIndex properties
@abcd-ca
Copy link

abcd-ca commented Feb 21, 2022

@arneroen, @jonathanpdiaz you should change your callback from a post to a get. That fixed the issue for me.

Hey @Brynjulf, what would that look like? Do you mean to change this line? res.redirect(requestUrl);

@Brynjulf
Copy link

@abcd-ca

app.post('/logout/callback', function(req, res) {
        req.logout();
        res.redirect('/');
});

should be changed to

app.get('/logout/callback', function(req, res) {
        req.logout();
        res.redirect('/');
});

Not sure if this is still valid as i haven't been using this package for years

@abcd-ca
Copy link

abcd-ca commented Feb 22, 2022

@Brynjulf thank you for your reply. I think you probably meant that in the reverse order, changing from get to post but I appreciate you clarifying which part you'd change.

For the benefit of anyone else trying to figure this out, res.redirect will create a GET request with a 302 (redirect) HTTP status code regardless of what method (get/post) the SP uses.

If you call res.redirect(307, requestUrl) then the 307 indicates to the browser to preserve the method from the parent route. if you use app.post... then res.redirect(307, requestUrl) will make a POST request to the IdP.

Note, requestUrl comes from strategy.logout(req, function(error, requestUrl)...

Regardless, with IdP using SAML 2.0 on ADFS, this does not work for me yet. Perhaps I need to add a signature to my logout request (though I am successfully doing SSO without signing – it's only SLO that is failing)

@srd90
Copy link

srd90 commented Feb 22, 2022

@abcd-ca @Brynjulf SAML SLO involves passing LogoutRequest and LogoutResponse messages back and forth.

Look at the picture at comment #221 (comment)

If your application is SP1 at that picture following things happen from you applications point of view (in following order):

  • user has clicked logout (single logout button)
  • your application must terminate local session
  • your application sends LogoutRequest to IdP via POST or redirect binding (which ever is better for you and which ever IdP support)
  • [ IdP propagates SLO to all services which participates to SSO session. Your application - SP1 - is not involved at this stage. When IdP has propagated LogoutRequest to all involved services and when it has received LogoutResponse response messages from all those application it shall report result of SLO to your application ]
  • your application's SLO callback receives LogoutResponse message from IdP via binding you have communicated to IdP (via metadata or by manually provisioning to IdP). LogoutResponse contains result of triggered SLO operation (whether it was successfull or whether partial logout from some services etc.)

If your application is e.g. SP2 at that picutre following things happens (and are expected from your application) and this scenario is IdP-initiated SLO from your SP/application point of view:

  • [ user has initiated SLO at some service ]
  • IdP propagates LogoutRequest to your application's logout callback via redirect or post binding (depending what you have configured to IdP via SP metadata or manually)
  • your application must terminate user's session
  • your application must send LogoutResponse message as a response to IdP. LogoutResponse must not say operation was success if your application was not able to terminate session designated by information at LogoutRequest sent to your application by IdP.

This issue ( #221 ) has multiple examples related to SLO. None of the examples contain proper SLO. None of those examples care to send LogoutResponse in case of IdP initiated-SLO. All of the examples incorrectly redirect to e.g. application main page. User's browser is at this point busy propagating SLO e.g. by running logout requests at iframes at IdP - navigation bar contains IdP's address - or if IdP uses especially error prone approach it has initiated redirect from SP to SP and if one SP is behaving badly logout propagation stops there. Examples in this issue contains badly behaving SPs. Furthermore there are few issues here and at stackoverflow where questions are something like "why does SLO work when it is initiated from my service but does not work if my service is second SP" or "why does SLO stops at service X's main/login page" and if those questions contain some code snippets usually it is quite obvious that LogoutResponses are not being sent by applications (for LogoutRequests sent by IdP) and SLO propagation fails.

passport-saml has broken support for IdP initiated logout (i.e. incoming LogoutRequest handling). You must delegate that request from your logout callback - counter intuitively - to passport-saml's authenticate method and at the end of the day processing ends up here (link poins to 3.2.1):

if (loggedOut) {
req.logout();
if (profile) {
if (this._saml == null) {
throw new Error("Can't get logout response URL without a SAML provider defined.");
}
const RelayState =
(req.query && req.query.RelayState) || (req.body && req.body.RelayState);
return this._saml.getLogoutResponseUrl(profile, RelayState, options, redirectIfSuccess);
}
return this.pass();
}
(reverse engineer from that code block and you'll see how it ends there).

Aforementioned code block does not work correctly with modern browsers. It fails to terminate session most of the time and it fails to report proper status at LogoutResponse (it has hardcoded success status).

For more information about problems of passport-saml's SLO see issue #419 ( some of the links at that issue point to bergie/passport-saml. For the record that issue was created before passport-saml repository, issues and development was moved over from bergie/passport-saml to node-saml/passport-saml).

btw. use e.g. some saml tracer browser plugin and sign in to few services which use SSO and SLO and share same IdP and which work correctly and observe SAML Logout request/response exchanged during SLO.

@johndegey
Copy link

Hi all,

I started a new Vue + node-express app today and try to implement saml with IDP-initiated SLO.
My version : "passport": "^0.6.0", "passport-saml": "^3.2.1".

I struggle with same problems, found the same boggus examples.

Finally I came to a "solution"...
Like @srd90 said, we need to use passpost.authenticate.

router.get('/logout/cb',
  passport.authenticate(
    'saml',
    {
      failureRedirect: '/logout/fail', failureMessage: false,
    },
  ),
);

Problem is in strategy.js file (found in passport-saml/lib/passport-saml).
In the validateCallback function definition, there is :
req.logout();

This call fails : it needs a callback...

So, I try to path this with :

// provide a fake one or a real one passed in options.fnCb
const cb = (options.fnCb?.bind(null, req) || (() => {}));
req.logout(cb);

All my tests are only done with IDP-initiated SLO and works...
I use Redis, and session is destroyed everytime.
See function processValidlySignedSamlLogoutAsync in passport-saml/lib/node-saml/saml.js

if (response) {
    return { profile: null, loggedOut: true };
}

The middleware can use a callback if needed and received the req as parameter:

router.get('/logout/cb',
  passport.authenticate(
    'saml',
    {
      fnCb: (req) => {
        if(req?.session?.passport && req.session.passport.user) {
          req.session.destroy();
        }
      },
      failureRedirect: '/logout/fail',
      failureMessage: false,
    },
  ),
);


'/logout/fail' : is a simple middleware to send my header to Vue app.

Maybe it can help...
or maybe my "patch" introduce some other problems...
but my app don't break the IDP-initiated SLO chain now.

Best

John

@srd90
Copy link

srd90 commented May 25, 2022

@johndegey if IdP initiated SLO (HTTP request with LogoutRequest message) comes to passport-saml without session cookie (due how samesite stuff works under various circumstances) there is no way that express-session (or any other library which uses cookies to session management) could associate correct authenticated HTTP session (the session which should be terminated) to req.

Up on receiving LogoutRequest passport-saml doesn't care if there is any session or if new unauthenicated session was created eagerly just for IdP initaited SLO request handling by experss-session (in which case it is not authenticated session which should be terminated) and passport-saml just responds with "Success" even if correct session was not actually terminated. For more information see #419 and its comments.

LogoutRequest contains information (session index and nameid) which could be used to find proper session from session store to be terminated. If this information would be stored (during authentication response handling) to session store it could be used to find that session to be destoyed or marked invalid/expired or something (be aware that possible concurrent request processing could end up writing session back to session store (see example scenario from this comment #619 (comment) ). NOTE: some evaka project might have implemented some secondary index based session termination at espoon-voltti/evaka#738 ... before you check out that project inspect its license because it seems to be more restrictive than MIT.

In order to IdP initiated SLO work correctly passport-saml must respond with LogoutResponse and if passport-saml reports "Success" (at the moment it is hardcoded to respond always "Success") it must have terminated correct session or else it must not respond status "Success".

I can't see how your implementation would work correctly if session cookie was not delivered along IdP initiated SLO request (due possible SameSite restrictions).

@johndegey
Copy link

Hello @srd90,

I think that it works in my case, because we are in "the best situation".
All apps are in the corporate network, share the same domain name and coookies are mandatory, you can't pass the idp without them.

I did not find any other solution than patching this file for having the SLO chain working like expected, again, in my situation.

Best,
John

@Simmoniz
Copy link

I found out why SLO doesn't work. I found a solution to make it work properly, im my case with Microsoft Azure (SAML-ADFS), using a database session storage. I log in from my app. Then I log out from microsoft azure ( not from my app), and it logs out nicely !

I think important to mention that there is a huge difference between the IDP logout request with all others. Most of the time, passport routes handle user device's initiated requests or redirects, That means the device's cookies comes along theses requests, which allows passport-saml to successfully retreive device's user information.

When the IDP broadcasts a logout request to relevant ISP(s), it is not always a device request (either initiated nor redirected). It is a simple HTTP GET request that contains a SAMLRequest. This SAMLRequest contains what the IDP uses to identify a saml user.

A saml user is identified using nameID and nameIDFormat profile properties.

Therefore, when a user logs in through an IDP, the app must store both values, nameID and nameIDFormat. You can acheive that first in you saml strategy authentication handler :

export const samlStrategy = new SamlStrategy({
      ...samlStrategyOptionsHere,
      logoutCallbackUrl: 'https://your.isp.com/saml/logout'
   }, 
   (profile: Profile | null | undefined, done: VerifiedCallback) => {
    
    // do login process. On successful login, don't forget 
    // to pass along nameID and nameIDFormat profile values !

    done(null, {
        userId: user.id, // in my case, my app just need to store the user id
        nameID: profile?.nameID,
        nameIDFormat: profile?.nameIDFormat
    })

});

Then store nameID and nameIDFormat in passport user's session ( within serialization process ) :

interface SessionUserData{
    userId: number,
    nameID?: string,
    nameIDFormat?: string,
}
passport.serializeUser<SessionUserData>((user: any, done) => {

    // do serialization stuff

    done(null, {
        userId: user.id,
        nameID: user?.nameID,
        nameIDFormat: user?.nameIDFormat
    })
}); 

SLO requests can then be handled properly by first retreiving passport sessions that holds the specified saml identification, and destroy them manually, like this :

interface SAMLLogoutExpectedResponse{
    profile?: Profile | null | undefined;
    loggedOut?: boolean | undefined;
}

authRouter.get('/saml/logout', async (req, res)=>{

    // req.user may not exists here, since this is a single HTTP-GET request that contains a SAMLRequest 
    // initiated by the IDP, not a user device redirection
    // use passport-saml to validate this request
    const response:SAMLLogoutExpectedResponse = await (samlStrategy._saml as SAML).validateRedirectAsync(req.query, url.parse(req.url).query)
    
    // check the « SAML USER » profile
    if( response?.profile && 
        response.profile?.nameID && 
        response.profile?.nameIDFormat && 
        response.loggedOut===true ){

        // get related session(s) if exists
        const sessionsToDestroy:string[] = ((await knex.from('sessions').where(true).select('*')) ?? []).reduce((acc:any[], session:any) => {
            const userData:any = JSON.parse(session?.data ?? {})?.passport?.user
            
            if( userData &&
                userData.nameID &&
                userData.nameIDFormat &&
                userData.nameID == response?.profile?.nameID &&
                userData.nameIDFormat == response?.profile?.nameIDFormat
                ){

                acc.push(session.session_id)
                
            }
            return acc;
        }, [])

        if(sessionsToDestroy.length){
            // destroy database session --> which invalidate any corresponding cookie on any device (if still exists)
            await knex.from('sessions').whereIn('session_id', sessionsToDestroy).delete();
        }
    }
    // if this is a IDP request, all ends here
    // if user initiated the logout request, IDP will redirect device here. Send the device somewhere coherent after successful logout
    res.redirect('/')
});

Voilà. I know this is a precise case, and doesn't fit to everyone. But the most important point is that a SLO callback must handle nameID and nameIDFormat values. It should not handle a device session.

@cjbarth
Copy link
Collaborator

cjbarth commented May 29, 2023

Is this still an open issue @arneroen ? If so, please reply to this thread and we'll reopen the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests