Skip to content

Multiple Entities ​

An application with many clients may need to support multiple identity providers. Different IdPs often require different SP configurations, so multiple SP instances may also be needed.

Multiple IdPs, single SP ​

javascript
// Define the SP.
const sp = saml.ServiceProvider({
  metadata: fs.readFileSync('./metadata_sp.xml')
});

// Define multiple IdPs.
const defaultIdP = saml.IdentityProvider({
  metadata: fs.readFileSync('./metadata_idp_default.xml')
});
const oneloginIdP = saml.IdentityProvider({
  metadata: fs.readFileSync('./metadata_onelogin.xml')
});

// SP-initiated SSO route, parameterised by IdP name.
router.get('/spinitsso-post/:idp', (req, res) => {
  let targetIdP;
  switch (req.params.idp) {
    case 'onelogin':
      targetIdP = oneloginIdP;
      break;
    default:
      targetIdP = defaultIdP;
      break;
  }
  return sp.createLoginRequest(targetIdP, 'post', (req, res) => res.render('actions', req));
});

With a single SP configuration, the request can be routed to different IdPs based on a path parameter. /spinitsso-post/onelogin authenticates against OneLogin; /spinitsso-post/default (or any unknown value) falls back to the default IdP.

Multiple IdPs, multiple SPs ​

Different IdPs may require different SP configurations — for example, OneLogin may require signed requests while the default IdP does not. The pattern below uses one SP instance per IdP.

javascript
// Define the default SP.
const defaultSP = saml.ServiceProvider({
  metadata: fs.readFileSync('./metadata_sp.xml')
});

// Define an SP specifically configured for OneLogin.
const oneloginSP = saml.ServiceProvider({
  metadata: fs.readFileSync('./metadata_sp_for_oneLogin.xml')
});

// Define the default IdP.
const defaultIdP = saml.IdentityProvider({
  metadata: fs.readFileSync('./metadata_idp_default.xml')
});

// Define the OneLogin IdP.
const oneloginIdP = saml.IdentityProvider({
  metadata: fs.readFileSync('./metadata_idp_onelogin.xml')
});

// SP-initiated SSO route, parameterised by IdP name.
router.get('/spinitsso-post/:idp', (req, res) => {
  let targetIdP;
  let sourceSP;
  switch (req.params.idp || '') {
    case 'onelogin':
      targetIdP = oneloginIdP;
      sourceSP = oneloginSP;
      break;
    default:
      targetIdP = defaultIdP;
      sourceSP = defaultSP;
      break;
  }
  return sourceSP.createLoginRequest(targetIdP, 'post', (req, res) => res.render('actions', req));
});