connect-saml2

Authenticate users via SAML 2.0 in connect or express

connect-saml2

SAML2 authentication for connect

This connect middleware allows an application to authenticate users via a SAML2 gateway, and access the attributes asserted by the gateway pertaining to those users. It doesn't require (and probably doesn't play nicely with) any other authentication framework. Currently connect ~2.0.0 is supported. As long as the urlencoded middleware doesn't go away, things should keep working for quite some time.

var connect = require("connect"),
    connect_saml2 = require("connect-saml2");
 
var app = connect();
 
// required only for the default user/session logic - see below for details 
app.use(connect.cookieParser());
app.use(connect.session({secret: "i am a secret lol"}));
 
app.use(connect_saml2({
  // force users to authenticate before they can pass through this middleware 
  ensureAuthentication: true,
  // this is information about your SAML gateway 
  idp: {
    singleSignOnService: "http://www.example.com/sso",
    fingerprint: "01:23:45:...",
  },
  // this is information about your application 
  sp: {
    entityId: "my-application",
  },
}));
 
// anyone getting to this point will have authenticated already, so you can rely 
// on req.user existing, because we specified `ensureAuthentication: true` above 
app.use(function(reqres) {
  res.writeHead(200, {
    "content-type": "application/json",
  });
 
  return res.end(JSON.stringify(req.user, null, 2));
});

This middleware is designed to sit right up near the top of your middleware stack, after your header parsing and session stuff but before your body parsing. This is so that it can parse urlencoded requests, and so that it can stop users from hitting your main application logic (if you choose to have it do so.)

Upon a request entering the middleware, connect_saml2 will check to see if there's a currently-valid SAML context that it knows about. It will, by default, do this by looking in the user's session. If there is a valid context, a few properties of req will be populated: samlAssertion, samlAssertionXml, and user. The request will then be passed along and allowed to continue.

If there is no current SAML context, and the ensureAuthentication option has been specified and is true, the user will be shuttled into the authentication process, involving a redirect to the SAML gateway, and from there back to our assertion consumer (which is part of this middleware.)

The properties mentioned above are as follows:

  • samlAssertion - a parsed and (somewhat) validated version of the original assertion sent by the SAML gateway.
  • samlAssertionXml - this is a DOM object representing the assertion, with namespaces and such intact.
  • user - this is a User object based on the assertion received

There is also an initiateAuthentication method that can be used to initiate the authentication process. It'd usually be used something like so:

app.use(function(reqresnext) {
  if (!req.user) {
    return req.initiateAuthentication();
  }
 
  res.end("hello, " + req.user.getAttribute("firstName"));
});

This object holds some information about a SAML context.

  • expiresAt - the time that the context expires
  • attributes - a map of attribute names to attribute value collections
  • getAttributes(name) - gets the collection of attributes under the name name
  • getAttribute(name) - as above, but only gets the first value in the collection (or null)
  • Type: object
  • Required: yes

This is an object that gets used as the instantiation options for the IdentityProvider object from the saml2 library. This includes things like the SSO service URL, the certificate or fingerprint, etc.

  • Type: object
  • Required: yes

This is like the idp parameter -- it's passed as-is into the constructor for a ServiceProvider object from the saml2 library. This contains the entity ID for the service and optionally the private key and certificate used to sign messages.

  • Type: boolean
  • Required: no
  • Default: false

Tells connect_saml2 whether or not to force all users through the authentication process before passing through to the next handler.

  • Type: string
  • Required: no
  • Default: ""

Useful in express applications where the middleware is located somewhere other than "/". In this case, set it to the mount point of the middleware (for example /auth/saml.)

  • Type: string
  • Required: no
  • Default: "/SAML2/AssertionConsumer/POST"

This controls what URL the middleware will pay attention to for consuming SAML assertions sent via the HTTP POST binding.

  • Type: function(assertionXml, req, cb)
  • Required: no
  • Default: (see below)
// default function 
function saveAssertionXml(assertionXmlreqcb) {
  req.session._saml = req.session._saml || {};
  req.session._saml.assertionXml = assertionXml;
 
  return cb();
};
  • Type: function(req, cb)
  • Required: no
  • Default: (see below)
// default function 
function fetchAssertionXml(reqcb) {
  return cb(null, (req.session && req.session._saml && req.session._saml.assertionXml) || null);
};
  • Type: function(req, cb)
  • Required: no
  • Default: (see below)
// default function 
function removeAssertionXml(reqcb) {
  if (req.session && req.session._saml && req.session._saml.assertionXml) {
    delete req.session._saml.assertionXml;
  }
 
  return cb();
};
  • Type: function(req, id, relayState, cb)
  • Required: no
  • Default: (see below)
// default function 
function saveRelayState(reqidrelayStatecb) {
  req.session._saml = req.session._saml || {};
  req.session._saml.relayState = req.session._saml.relayState || {};
  req.session._saml.relayState[id] = relayState;
 
  return cb();
};
  • Type: function(req, id, cb)
  • Required: no
  • Default: (see below)
// default function 
function fetchRelayState(reqidcb) {
  var relayState = null;
 
  if (req.session && req.session._saml && req.session._saml.relayState && req.session._saml.relayState[id]) {
    relayState = req.session._saml.relayState[id];
 
    delete req.session._saml.relayState[id];
  }
 
  return cb(null, relayState);
};
  • Type: function(req, cb)
  • Required: no
  • Default: (see below)
// default function 
function afterAuthentication(reqcb) {
  req.session._saml = req.session._saml || {};
  req.session._saml.fresh = true;
 
  return cb();
};

3-clause BSD. A copy is included with the source.