Strategies

Strategies are responsible for authenticating requests, which they accomplish by implementing an authentication mechanism. Authentication mechanisms define how to encode a credential, such as a password or an assertion from an identity provider (IdP), in a request. They also specify the procedure necessary to verify that credential. If the credential is successfully verified, the request is authenticated.

There are a wide variety of authentication mechanisms, and a corresponding variety of strategies. Strategies are distributed in separate packages which must be installed, configured, and registered.

Install

Strategies are published to the npm registry, and installed using a package manager.

For example, the following command will install passport-local, a package which provides a strategy for authenticating with a username and password:

$ npm install passport-local

And the following command will install passport-openidconnect, a package which implements support for OpenID Connect:

$ npm install passport-openidconnect

Developers only need to install the packages which provide authentication mechanisms required by the application. These packages are then plugged into Passport. This reduces overall application size by avoiding unnecessary dependencies.

Configure

Once a package has been installed, the strategy needs to be configured. The configuration varies with each authentication mechanism, so strategy-specific documentation should be consulted. That being said, there are common patterns that are encountered across many strategies.

The following code is an example that configures the LocalStrategy:

var LocalStrategy = require('passport-local');

var strategy = new LocalStrategy(function verify(username, password, cb) {
  db.get('SELECT * FROM users WHERE username = ?', [ username ], function(err, user) {
    if (err) { return cb(err); }
    if (!user) { return cb(null, false, { message: 'Incorrect username or password.' }); }

    crypto.pbkdf2(password, user.salt, 310000, 32, 'sha256', function(err, hashedPassword) {
      if (err) { return cb(err); }
      if (!crypto.timingSafeEqual(user.hashed_password, hashedPassword)) {
        return cb(null, false, { message: 'Incorrect username or password.' });
      }
      return cb(null, user);
    });
  });
});

Verify Function

The LocalStrategy constructor takes a function as an argument. This function is known as a verify function, and is a common pattern in many strategies. When authenticating a request, a strategy parses the credential contained in the request. A verify function is then called, which is responsible for determining the user to which that credential belongs. This allows data access to be delegated to the application.

In this particular example, the verify function is executing a SQL query to obtain a user record from the database and, after verifying the password, yielding the record back to the strategy, thus authenticating the user and establishing a login session.

Because a verify function is supplied by the application itself, access to persistent storage is not constrained in any way. The application is free to use any data storage system, including relational databases, graph databases, or document stores, and structure data within that database according to any schema.

A verify function is strategy-specific, and the exact arguments it receives and parameters it yields will depend on the underlying authentication mechanism. For authentication mechanisms involving shared secrets, such as a password, a verify function is responsible for verifying the credential and yielding a user. For mechanisms that provide cryptographic authentication, a verify function will typically yield a user and a key, the later of which the strategy will use to cryptographically verify the credential.

A verify function yields under one of three conditions: success, failure, or an error.

If the verify function finds a user to which the credential belongs, and that credential is valid, it calls the callback with the authenticating user:

return cb(null, user);

If the credential does not belong to a known user, or is not valid, the verify function calls the callback with false to indicate an authentication failure:

return cb(null, false);

If an error occurs, such as the database not being available, the callback is called with an error, in idiomatic Node.js style:

return cb(err);

It is important to distinguish between the two failure cases that can occur. Authentication failures are expected conditions, in which the server is operating normally, even though invalid credentials are being received from the user (or a malicious adversary attempting to authenticate as the user). Only when the server is operating abnormally should err be set, to indicate an internal error.

Register

With the strategy configured, it is then registered by calling .use():

var passport = require('passport');

passport.use(strategy);

All strategies have a name which, by convention, corresponds to the package name according to the pattern passport-{name}. For instance, the LocalStrategy configured above is named local as it is distributed in the passport-local package.

Once registered, the strategy can be employed to authenticate a request by passing the name of the strategy as the first argument to passport.authenticate() middleware:

app.post('/login/password',
  passport.authenticate('local', { failureRedirect: '/login', failureMessage: true }),
  function(req, res) {
    res.redirect('/~' + req.user.username);
  });

In cases where there is a naming conflict, or the default name is not sufficiently descriptive, the name can be overridden when registering the strategy by passing a name as the first argument to .use():

var passport = require('passport');

passport.use('password', strategy);

That name is then specified to passport.authenticate() middleware:

app.post('/login/password',
  passport.authenticate('password', { failureRedirect: '/login', failureMessage: true }),
  function(req, res) {
    res.redirect('/~' + req.user.username);
  });

For brevity, strategies are often configured and registered in a single statement:

var passport = require('passport');
var LocalStrategy = require('passport-local');

passport.use(new LocalStrategy(function verify(username, password, cb) {
  // ...
});

SEARCH FOR STRATEGIES

0STRATEGIES