Authenticate using Strategy Instances

Jared Hanson
Passport.js
Published in
2 min readDec 9, 2019

An enhancement to pass Strategy instances to authenticate() has been added to passport version 0.4.1. This makes it easier to dynamically alter authentication based on parameters of a request.

Let’s take a look at how this new feature can be used to implement support for multi-tenancy. In this example, we have a single application that services requests for two different domains: contoso.comand fabrikam.com. Both domains let people login using Facebook, but each domain uses a unique App ID.

/**
* Create a strategy for given host.
*
* @param {string} host
*/
function createStrategy(host) {
var strategy, clientID, clientSecret;
// Switch on the host argument, assigning the corresponding
// App ID and secret.
switch(host) {
case 'www.contoso.com':
clientID = process.env['CONTOSO_APP_ID'];
clientSecret = process.env['CONTOSO_APP_SECRET'];
break;
case 'www.fabrikam.com':
clientID = process.env['FABRIKAM_APP_ID'];
clientSecret = process.env['FABRIKAM_APP_SECRET'];
break;
}
// Create and return a new Facebook strategy, using the
// App ID and secret assigned above.
strategy = new FacebookStrategy({
clientID: clientID,
clientSecret: clientSecret,
callbackURL: '/login/facebook/callback',
},
function(accessToken, refreshToken, profile, cb) {
return cb(null, profile);
});
return strategy;
}
/**
* Login middleware.
*
* This middleware wraps `passport.authenticate` to login with
* a host-specific strategy.
*/
function login(req, res, next) {
var host = req.headers.host;
var strategy = createStrategy(host);
passport.authenticate(strategy)(req, res, next);
}
// Mount login routes using dynamic login middleware.
app.get('/login/facebook', login);
app.get('/login/facebook/callback', login);

As illustrated above, a new “dynamic” login middleware is used. This middleware wraps authenticate(), passing a Strategy instance constructed dynamically from parameters in the request, in this case the host header.

While the example above is deliberately simple, for illustrative purposes, it can easily be extended to support thousands of tenants by loading configuration from a database. Such scenarios are common in business-to-business (B2B) applications, in which people login using a SAML or OpenID Connect identity provider managed by the organization they work for.

Because any parameter of the request can be used to create dynamically create a new strategy, or alter the behavior of a verify callback, this functionality has a wide variety of usages, beyond multi-tenancy. Another common pattern is handling login and account linking accordingly, while consolidating on a single callback URL.

To update, run the following command:

npm install passport@0.4.1 --save

No other features or fixes were introduced in this version, and previously existing functionality is unchanged.

--

--