passport-refresh-token
Version:
Passport strategy to authenticate using a previously issued refresh token, and provide new access tokens for Oauth 2.0 flow.
157 lines (139 loc) • 4.17 kB
JavaScript
/**
* Module dependencies.
*/
var passport = require('passport-strategy')
, util = require('util');
/**
* Creates an instance of `Strategy`.
*
* The Reresh Token strategy authenticates requests based on a refresh token
* contained in the `Authorization` header field, `refresh_token` body
* parameter, or `refresh_token` query parameter.
*
* Applications must supply a `verify` callback, for which the function
* signature is:
*
* function(token, done) { ... }
*
* `token` is the refresh token provided as a credential. The verify callback
* is responsible for finding the user who posesses the token, and invoking
* `done` with the following arguments:
*
* done(err, user, info);
*
* If the token is not valid, `user` should be set to `false` to indicate an
* authentication failure. Additional token `info` can optionally be passed as
* a third argument, which will be set by Passport at `req.authInfo`, where it
* can be used by later middleware for access control. This is typically used
* to pass any scope associated with the token.
*
* Options:
*
* - `realm` authentication realm, defaults to "Users"
* - `scope` list of scope values indicating the required scope of the access
* token for accessing the requested resource
*
* Examples:
*
* passport.use(new RefreshTokenStrategy(
* function(token, done) {
* User.findByToken({ token: token }, function (err, user) {
* if (err) { return done(err); }
* if (!user) { return done(null, false); }
* return done(null, user, { scope: 'read' });
* });
* }
* ));
*
* @constructor
* @param {Object} [options]
* @param {Function} verify
* @api public
*/
function Strategy(options, verify) {
if (typeof options == 'function') {
verify = options;
options = {};
}
if (!verify) { throw new TypeError('RefreshTokenStrategy requires a verify callback'); }
passport.Strategy.call(this);
this.name = 'refresh_token';
this._verify = verify;
this._realm = options.realm || 'Users';
if (options.scope) {
this._scope = (Array.isArray(options.scope)) ? options.scope : [ options.scope ];
}
this._passReqToCallback = options.passReqToCallback;
}
/**
* Inherit from `passport.Strategy`.
*/
util.inherits(Strategy, passport.Strategy);
/**
* Authenticate request based on the contents of body parameter or query parameter.
*
* @param {Object} req
* @api protected
*/
Strategy.prototype.authenticate = function(req) {
var token;
if (req.body && req.body.refresh_token) {
if (token) { return this.fail(400); }
token = req.body.refresh_token;
}
if (req.body && req.body.refreshToken) {
if (token) { return this.fail(400); }
token = req.body.refreshToken;
}
if (req.query && req.query.refresh_token) {
if (token) { return this.fail(400); }
token = req.query.refresh_token;
}
if (req.query && req.query.refreshToken) {
if (token) { return this.fail(400); }
token = req.query.refreshToken;
}
if (!token) { return this.fail(this._challenge()); }
var self = this;
function verified(err, user, info) {
if (err) { return self.error(err); }
if (!user) {
if (typeof info == 'string') {
info = { message: info }
}
info = info || {};
return self.fail(self._challenge('invalid_token', info.message));
}
self.success(user, info);
}
if (self._passReqToCallback) {
this._verify(req, token, verified);
} else {
this._verify(token, verified);
}
};
/**
* Build authentication challenge.
*
* @api private
*/
Strategy.prototype._challenge = function(code, desc, uri) {
var challenge = 'RefreshToken realm="' + this._realm + '"';
if (this._scope) {
challenge += ', scope="' + this._scope.join(' ') + '"';
}
if (code) {
challenge += ', error="' + code + '"';
}
if (desc && desc.length) {
challenge += ', error_description="' + desc + '"';
}
if (uri && uri.length) {
challenge += ', error_uri="' + uri + '"';
}
return challenge;
};
/**
* Expose `Strategy`.
*/
module.exports = Strategy;