passport-facebook-token
Version:
Facebook token authentication strategy for Passport
207 lines (169 loc) • 8.85 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _url = require('url');
var _url2 = _interopRequireDefault(_url);
var _crypto = require('crypto');
var _crypto2 = _interopRequireDefault(_crypto);
var _passportOauth = require('passport-oauth');
/**
* `FacebookTokenStrategy` constructor.
*
* The Facebook authentication strategy authenticates requests by delegating to
* Facebook using the OAuth 2.0 protocol.
*
* Applications must supply a `verify` callback which accepts an `accessToken`,
* `refreshToken` and service-specific `profile`, and then calls the `done`
* callback supplying a `user`, which should be set to `false` if the
* credentials are not valid. If an exception occurred, `error` should be set.
*
* @param {Object} options
* @param {Function} verify
* @example
* passport.use(new FacebookTokenStrategy({
* clientID: '123456789',
* clientSecret: 'shhh-its-a-secret'
* }), (accessToken, refreshToken, profile, done) => {
* User.findOrCreate({facebookId: profile.id}, done);
* });
*/
var FacebookTokenStrategy = (function (_OAuth2Strategy) {
_inherits(FacebookTokenStrategy, _OAuth2Strategy);
function FacebookTokenStrategy(_options, _verify) {
_classCallCheck(this, FacebookTokenStrategy);
var options = _options || {};
var verify = _verify;
options.authorizationURL = options.authorizationURL || 'https://www.facebook.com/v2.4/dialog/oauth';
options.tokenURL = options.tokenURL || 'https://graph.facebook.com/oauth/access_token';
_get(Object.getPrototypeOf(FacebookTokenStrategy.prototype), 'constructor', this).call(this, options, verify);
this.name = 'facebook-token';
this._accessTokenField = options.accessTokenField || 'access_token';
this._refreshTokenField = options.refreshTokenField || 'refresh_token';
this._profileURL = options.profileURL || 'https://graph.facebook.com/v2.4/me';
this._profileFields = options.profileFields || ['id', 'name', 'emails'];
this._clientSecret = options.clientSecret;
this._enableProof = typeof options.enableProof === 'boolean' ? options.enableProof : true;
this._passReqToCallback = options.passReqToCallback;
this._oauth2.useAuthorizationHeaderforGET(false);
}
/**
* Authenticate request by delegating to a service provider using OAuth 2.0.
* @param {Object} req
* @param {Object} options
*/
_createClass(FacebookTokenStrategy, [{
key: 'authenticate',
value: function authenticate(req, options) {
var _this = this;
var accessToken = req.body && req.body[this._accessTokenField] || req.query && req.query[this._accessTokenField];
var refreshToken = req.body && req.body[this._refreshTokenField] || req.query && req.query[this._refreshTokenField];
if (!accessToken) return this.fail({ message: 'You should provide ' + this._accessTokenField });
this._loadUserProfile(accessToken, function (error, profile) {
if (error) return _this.error(error);
var verified = function verified(error, user, info) {
if (error) return _this.error(error);
if (!user) return _this.fail(info);
return _this.success(user, info);
};
if (_this._passReqToCallback) {
_this._verify(req, accessToken, refreshToken, profile, verified);
} else {
_this._verify(accessToken, refreshToken, profile, verified);
}
});
}
/**
* Retrieve user profile from Facebook.
*
* This function constructs a normalized profile, with the following properties:
*
* - `provider` always set to `facebook`
* - `id` the user's Facebook ID
* - `username` the user's Facebook username
* - `displayName` the user's full name
* - `name.familyName` the user's last name
* - `name.givenName` the user's first name
* - `name.middleName` the user's middle name
* - `gender` the user's gender: `male` or `female`
* - `profileUrl` the URL of the profile for the user on Facebook
* - `emails` the proxied or contact email address granted by the user
*
* @param {String} accessToken
* @param {Function} done
*/
}, {
key: 'userProfile',
value: function userProfile(accessToken, done) {
var url = _url2['default'].parse(this._profileURL);
if (this._enableProof) {
// For further details, refer to https://developers.facebook.com/docs/reference/api/securing-graph-api/
var proof = _crypto2['default'].createHmac('sha256', this._clientSecret).update(accessToken).digest('hex');
url.search = (url.search ? url.search + '&' : '') + 'appsecret_proof=' + encodeURIComponent(proof);
}
if (this._profileFields) {
var fields = FacebookTokenStrategy.convertProfileFields(this._profileFields);
url.search = (url.search ? url.search + '&' : '') + 'fields=' + fields;
}
url = _url2['default'].format(url);
this._oauth2.get(url, accessToken, function (error, body, res) {
if (error) return done(new _passportOauth.InternalOAuthError('Failed to fetch user profile', error));
try {
var json = JSON.parse(body);
var profile = {
provider: 'facebook',
id: json.id,
displayName: json.name || '',
name: {
familyName: json.last_name || '',
givenName: json.first_name || '',
middleName: json.middle_name || ''
},
gender: json.gender || '',
emails: [{
value: json.email || ''
}],
photos: [{
value: 'https://graph.facebook.com/' + json.id + '/picture?type=large'
}],
_raw: body,
_json: json
};
done(null, profile);
} catch (e) {
done(e);
}
});
}
/**
* Converts array of profile fields to string
* @param {Array} _profileFields Profile fields i.e. ['id', 'email']
* @returns {String}
*/
}], [{
key: 'convertProfileFields',
value: function convertProfileFields(_profileFields) {
var profileFields = _profileFields || [];
var map = {
'id': 'id',
'displayName': 'name',
'name': ['last_name', 'first_name', 'middle_name'],
'gender': 'gender',
'profileUrl': 'link',
'emails': 'email',
'photos': 'picture'
};
return profileFields.reduce(function (acc, field) {
return acc.concat(map[field] || field);
}, []).join(',');
}
}]);
return FacebookTokenStrategy;
})(_passportOauth.OAuth2Strategy);
exports['default'] = FacebookTokenStrategy;
module.exports = exports['default'];