leftmostcat/passport-oauth2-password-grant

View on GitHub
lib/strategy.js

Summary

Maintainability
A
1 hr
Test Coverage
var passport = require('passport-strategy'),
    OAuth2Strategy = require('passport-oauth2'),
    util = require('util'),
    OAuth2 = require('oauth').OAuth2,
    AuthorizationError = require('passport-oauth2').AuthorizationError,
    TokenError = require('passport-oauth2').TokenError;

function PasswordGrantStrategy(options, verify) {
  if (typeof options == 'function') {
    verify = options;
    options = undefined;
  }
  options = options || {};

  if (!verify) { throw new TypeError('PasswordGrantStrategy requires a verify callback'); }
  if (!options.tokenURL) { throw new TypeError('PasswordGrantStrategy requires a tokenURL option'); }
  if (!options.clientID) { throw new TypeError('PasswordGrantStrategy requires a clientID option'); }

  /* The OAuth2Strategy constructor is insufficiently generic to allow us to
   * call it directly. */
  passport.Strategy.call(this);
  this.name = 'password-grant';
  this._verify = verify;

  this._oauth2 = new OAuth2(options.clientID, options.clientSecret, '', '', options.tokenURL, options.customHeaders);

  this._passReqToCallback = options.passReqToCallback;
  this._skipUserProfile = (options.skipUserProfile === undefined) ? false : options.skipUserProfile;
}

util.inherits(PasswordGrantStrategy, OAuth2Strategy);

/* Adapted from passport-oauth2. Given its fixed grant type, we reimplement
 * portions of its access token logic. */
PasswordGrantStrategy.prototype.authenticate = function(req, options) {
  options = options || {};
  var self = this;

  if (!options.username) { throw new TypeError('PasswordGrantStrategy requires a username param'); }
  if (!options.password) { throw new TypeError('PasswordGrantStrategy requires a password param'); }

  var params = {};
  params.grant_type = 'password';
  params.username = options.username;
  params.password = options.password;

  this._oauth2.getOAuthAccessToken(null, params,
    function(err, accessToken, refreshToken, params) {
      if (err) { return self.error(self._createOAuthError('Failed to obtain access token', err)); }

      self._loadUserProfile(accessToken, function(err, profile) {
        if (err) { return self.error(err); }

        function verified(err, user, info) {
          if (err) { return self.error(err); }
          if (!user) { return self.fail(info); }
          self.success(user, info);
        }

        try {
          var arity = self._verify.length;

          if (self._passReqToCallback) {
            if (arity == 6) {
              self._verify(req, accessToken, refreshToken, params, profile, verified);
            } else { // arity == 5
              self._verify(req, accessToken, refreshToken, profile, verified);
            }
          } else {
            if (arity == 5) {
              self._verify(accessToken, refreshToken, params, profile, verified);
            } else { // arity == 4
              self._verify(accessToken, refreshToken, profile, verified);
            }
          }
        } catch (ex) {
          return self.error(ex);
        }
      });
    });
};

PasswordGrantStrategy.prototype.parseErrorResponse = function(body, status) {
  var json = JSON.parse(body);

  if (json.error) {
    switch (json.error) {
      case 'invalid_grant':
        return new AuthorizationError(json.error_description, json.error, json.error_uri);
        break;
      default:
        return new TokenError(json.error_description, json.error, json.error_uri);
    }
  }

  return null;
}

module.exports = PasswordGrantStrategy;