Skip to content

Commit

Permalink
Merge branch 'CosmicColor-master'
Browse files Browse the repository at this point in the history
* CosmicColor-master:
  Release 2.0
  Bump version
  use 2.2 version of facebook api
  Add coverage badge to readme
  Update .editorconfig
  • Loading branch information
drudge committed Apr 29, 2015
2 parents d3d2b00 + b2a8682 commit ea56839
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 220 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ root = true

[*]
indent_style = space
indent_size = 4
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# passport-facebook-token
# passport-facebook-token

[![Build Status](https://travis-ci.org/drudge/passport-facebook-token.svg)](https://travis-ci.org/drudge/passport-facebook-token)
[![Dependency Status](https://david-dm.org/drudge/passport-facebook-token.svg)](https://david-dm.org/drudge/passport-facebook-token)
[![Coverage Status](https://coveralls.io/repos/drudge/passport-facebook-token/badge.svg)](https://coveralls.io/r/drudge/passport-facebook-token)

[Passport](http://passportjs.org/) strategy for authenticating with [Facebook](http://www.facebook.com/)
access tokens using the OAuth 2.0 API.
Expand Down
214 changes: 209 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,211 @@
var pkginfo = require('pkginfo');
var Strategy = require('./lib/strategy');
var util = require('util');
var uri = require('url');
var crypto = require('crypto');
var OAuth2Strategy = require('passport-oauth2');
var InternalOAuthError = require('passport-oauth2').InternalOAuthError;

pkginfo(module, 'version');
util.inherits(FacebookTokenStrategy, OAuth2Strategy);

module.exports = Strategy;
module.exports.Strategy = Strategy;
/**
* `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.
*
* Options:
* - `clientID` your Facebook application's App ID
* - `clientSecret` your Facebook application's App Secret
*
* Examples:
*
* passport.use(new FacebookTokenStrategy({
* clientID: '123-456-789',
* clientSecret: 'shhh-its-a-secret'
* },
* function(accessToken, refreshToken, profile, done) {
* User.findOrCreate(..., function (err, user) {
* done(err, user);
* });
* }
* ));
*
* @param {Object} options
* @param {Function} verify
* @api public
*/
function FacebookTokenStrategy(options, verify) {
options = options || {};
options.authorizationURL = options.authorizationURL || 'https://www.facebook.com/v2.2/dialog/oauth';
options.tokenURL = options.tokenURL || 'https://graph.facebook.com/oauth/access_token';
options.scopeSeparator = options.scopeSeparator || ',';

OAuth2Strategy.call(this, options, verify);

this.name = 'facebook-token';
this._passReqToCallback = options.passReqToCallback;
this._profileURL = options.profileURL || 'https://graph.facebook.com/v2.2/me';
this._clientSecret = options.clientSecret;
this._enableProof = options.enableProof;
this._profileFields = options.profileFields || null;
this._oauth2._useAuthorizationHeaderForGET = false;
}

/**
* Authenticate request by delegating to a service provider using OAuth 2.0.
* @param {Object} req
* @param {Object} options
* @api protected
*/
FacebookTokenStrategy.prototype.authenticate = function(req, options) {
var self = this;
accessToken = (req.body && req.body.access_token) || (req.query && req.query.access_token) || (req.headers && req.headers.access_token),
refreshToken = (req.body && req.body.refresh_token) || (req.query && req.query.refresh_token) || (req.headers && req.headers.refresh_token);

if (!accessToken) {
return this.fail({
message: 'You should provide access_token'
});
}

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

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

return self.success(user, info);
}

if (self._passReqToCallback) {
self._verify(req, accessToken, refreshToken, profile, verified);
} else {
self._verify(accessToken, refreshToken, profile, verified);
}
});
};

/**
* Return extra Facebook-specific parameters to be included in the authorization
* request.
*
* Options:
* - `display` Display mode to render dialog, { `page`, `popup`, `touch` }.
*
* @param {Object} options
* @return {Object}
* @api protected
*/
FacebookTokenStrategy.prototype.authorizationParams = function(options) {
return options.display ? {
display: options.display
} : {};
};

/**
* 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
* @api protected
*/
FacebookTokenStrategy.prototype.userProfile = function(accessToken, done) {
var url = uri.parse(this._profileURL);

if (this._enableProof) {
// For further details, refer to https://developers.facebook.com/docs/reference/api/securing-graph-api/
var proof = crypto.createHmac('sha256', this._clientSecret).update(accessToken).digest('hex');
url.search = (url.search ? url.search + '&' : '') + 'appsecret_proof=' + encodeURIComponent(proof);
}

if (this._profileFields) {
var fields = this._convertProfileFields(this._profileFields);
if (fields !== '') {
url.search = (url.search ? url.search + '&' : '') + 'fields=' + fields;
}
}

url = uri.format(url);

this._oauth2.get(url, accessToken, function(error, body, res) {
if (error) return done(new InternalOAuthError('Failed to fetch user profile', error));

try {
var json = JSON.parse(body),
profile = {
provider: 'facebook',
id: json.id,
displayName: json.name || '',
name: {
familyName: json.last_name || '',
givenName: json.first_name || '',
middleName: json.middle_name || ''
},
emails: [{
value: json.email || ''
}],
photos: [{
value: ['https://graph.facebook.com/', json.id, '/picture?type=large'].join('') || ''
}],
_raw: body,
_json: json
};

done(null, profile);
} catch (e) {
done(e);
}
});
};

FacebookTokenStrategy.prototype._convertProfileFields = function(profileFields) {
var map = {
'id': 'id',
'username': 'username',
'displayName': 'name',
'name': ['last_name', 'first_name', 'middle_name'],
'gender': 'gender',
'profileUrl': 'link',
'emails': 'email',
'photos': 'picture'
},
fields = [];

profileFields.forEach(function(field) {
if (typeof map[field] === 'undefined') {
return fields.push(field);
}

if (Array.isArray(map[field])) {
Array.prototype.push.apply(fields, map[field]);
} else {
fields.push(map[field]);
}
});

return fields.join(',');
};

/**
* Expose `FacebookTokenStrategy`.
*/
module.exports = FacebookTokenStrategy;
module.exports.Strategy = FacebookTokenStrategy;
Loading

0 comments on commit ea56839

Please sign in to comment.