13
votes

I have a question regarding the proper way to logout a user when using passport-saml for authentication.

The example script with passport-saml shows logging out as this:

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

From what I can tell, this will end the local passport session, but it doesn't seem to send a logout request to the SAML IdP. When the user does another login, it redirects to the IdP but immediately redirects back with the authenticated user. Is there a way to logout with the IdP so that the user has to enter their password again when signing in to my site? I've seen other sites that use our IdP do this, so I think it's possible.

I did notice in the passport-saml code that there is a logout() method on the passport-saml Strategy object, which doesn't seem to be called by req.logout(). So I tried switching the code to this:

app.get('/logout', function(req, res) {
    //strategy is a ref to passport-saml Strategy instance 
    strategy.logout(req, function(){
        req.logout();
        res.redirect('/');
    });
});

But I got this error deep in XMLNode.js

Error: Could not create any elements with: [object Object]
   at XMLElement.module.exports.XMLNode.element (/.../node_modules/passport-saml/node_modules/xmlbuilder/lib/XMLNode.js:74:15)
   at XMLElement.module.exports.XMLNode.element (/.../node_modules/passport-saml/node_modules/xmlbuilder/lib/XMLNode.js:54:25)
   at XMLElement.module.exports.XMLNode.element (/.../node_modules/passport-saml/node_modules/xmlbuilder/lib/XMLNode.js:54:25)
   at new XMLBuilder (/.../node_modules/passport-saml/node_modules/xmlbuilder/lib/XMLBuilder.js:27:19)
   at Object.module.exports.create (/.../node_modules/passport-saml/node_modules/xmlbuilder/lib/index.js:11:12)
   at SAML.generateLogoutRequest (/.../node_modules/passport-saml/lib/passport-saml/saml.js:169:21)

Am I not calling this method correctly? Or should I not be calling this method directly and calling some other method instead?

I see that in generateLogoutRequest() it is referring to two properties on the req.user that I'm not sure are there:

  'saml:NameID' : {
    '@Format': req.user.nameIDFormat,
    '#text': req.user.nameID
  }

If these properties are not there, will that cause this error? If so, I assume that maybe I need to ensure that these properties are added to the user object that is returned from the verify callback function?

Thanks for any help anyone might be able to provide on this.

1

1 Answers

11
votes

Yes adding the nameIDFormat and nameID to the user will solve the issue.

  1. To enable the logout you should configure the logoutURL option in your strategy

logoutUrl: 'http://example.org/simplesaml/saml2/idp/SingleLogoutService.php',

The logout method in the strategy does not actually send any request. the callback function is called with the request as parameter.

To launch the logout process :

passport.logoutSaml = function(req, res) {
    //Here add the nameID and nameIDFormat to the user if you stored it someplace.
    req.user.nameID = req.user.saml.nameID;
    req.user.nameIDFormat = req.user.saml.nameIDFormat;


    samlStrategy.logout(req, function(err, request){
        if(!err){
            //redirect to the IdP Logout URL
            res.redirect(request);
        }
    });
};

edit: the nameId and nameIdFormat has to be saved somewhere on successful login

var samlStrategy = new SamlStrategy(
  {
    callbackUrl: 'https://mydomain/auth/saml/callback',
    entryPoint: 'https://authprovider/endpoint',
    logoutUrl: 'https://authprovider/logoutEndPoint',
    issuer: 'passport-saml'
  },
  function(profile, done) {

      //Here save the nameId and nameIDFormat somewhere
      user.saml = {};
      user.saml.nameID = profile.nameID;
      user.saml.nameIDFormat = profile.nameIDFormat;

      //Do save

      });
  });
  1. You will also have to create an end point for the logout callback :

This URL should be configured in your SP metadata in your IdP configuration. The IdP will redirect to that URL once the logout is done.

in your routes :

app.post('/auth/saml/logout/callback', passport.logoutSamlCallback);

In your passport configuration :

passport.logoutSamlCallback = function(req, res){
    req.logout();
    res.redirect('/');
}