diff --git a/README.md b/README.md index b577e5c..63a6412 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,24 @@ npm i oauth2orize-facebook -S ## Usage +Before using this package, you should enable the 'Require App Secret' option in the advanced settings of your Facebook app. +You also must provide the app secret by exporting it + +```sh +export FB_APP_SECRET={your Facebook app secret} +``` + +or by adding FB_APP_SECRET={your Facebook app secret} to your .env file. + +Then, you can have fun + ```js var oauth2orize = require('oauth2orize'); var oauth2orizeFacebook = require('oauth2orize-facebook'); var server = oauth2orize.createServer(); -server.exchange(oauth2orizeFacebook(function (client, profile, scope, cb) { +server.exchange(oauth2orizeFacebook(['email', 'first_name', 'last_name'], function (client, profile, scope, cb) { // Get access token from client and Facebook profile information. var accessToken = 'access token'; diff --git a/lib/index.js b/lib/index.js index 132117e..872b7dd 100644 --- a/lib/index.js +++ b/lib/index.js @@ -5,10 +5,22 @@ var objectAssign = require('object-assign'); var getFacebookProfile = require('./util').getFacebookProfile; -module.exports = function (opts, issue) { +module.exports = function (opts, fields, issue) { + if (Array.isArray(opts)) { + issue = fields; + fields = opts; + opts = null; + } + if (typeof opts === 'function') { issue = opts; opts = null; + fields = null; + } + + if (fields != null && !Array.isArray(fields)) { + throw new Error('You must specify the Facebook profile fields as an array.' + + 'Also, make sure you have permission to access these fields'); } if (typeof issue !== 'function') { @@ -28,7 +40,7 @@ module.exports = function (opts, issue) { return function facebook(req, res, next) { if (!req.body) { return next(new Error('Request body not parsed. ' + - 'Use bodyParser middleware.')); + 'Use bodyParser middleware.')); } // The `user` property of `req` holds the authenticated user. In the case @@ -40,65 +52,66 @@ module.exports = function (opts, issue) { if (!token) { return next(new AuthorizationError( - 'Missing Facebook access token!', 'invalid_request')); + 'Missing Facebook access token!', 'invalid_request')); } - getFacebookProfile(token, function (err, profile) { - if (err) { - return next(new AuthorizationError( - 'Could not get Facebook profile using provided access token.', - 'invalid_request' - )); - } - - if (scope) { - for (var i = 0, len = separators.length; i < len; i++) { - // Only separates on the first matching separator. - // This allows for a sort of separator "priority" - // (ie, favors spaces then fallback to commas). - var separated = scope.split(separators[i]); - - if (separated.length > 1) { - scope = separated; - break; - } + getFacebookProfile(fields, token, function (err, profile) { + if (err) { + console.log(err); + return next(new AuthorizationError( + 'Could not get Facebook profile using provided access token.', + 'invalid_request' + )); } - if (!Array.isArray(scope)) { - scope = [ scope ]; - } - } + if (scope) { + for (var i = 0, len = separators.length; i < len; i++) { + // Only separates on the first matching separator. + // This allows for a sort of separator "priority" + // (ie, favors spaces then fallback to commas). + var separated = scope.split(separators[i]); + + if (separated.length > 1) { + scope = separated; + break; + } + } - var issued = function (err, accessToken, refreshToken, params) { - if (err) { - return next(err); + if (!Array.isArray(scope)) { + scope = [ scope ]; + } } - if (!accessToken) { - return next(new AuthorizationError( - 'Permissions was not granted.', 'invalid_grant')); - } + var issued = function (err, accessToken, refreshToken, params) { + if (err) { + return next(err); + } + + if (!accessToken) { + return next(new AuthorizationError( + 'Permissions was not granted.', 'invalid_grant')); + } - var json = { 'access_token': accessToken }; + var json = { 'access_token': accessToken }; - if (refreshToken) { - json['refresh_token'] = refreshToken; - } + if (refreshToken) { + json['refresh_token'] = refreshToken; + } - if (params) { - objectAssign(json, params); - } + if (params) { + objectAssign(json, params); + } - json['token_type'] = json['token_type'] || 'bearer'; - json = JSON.stringify(json); + json['token_type'] = json['token_type'] || 'bearer'; + json = JSON.stringify(json); - res.setHeader('Content-Type', 'application/json'); - res.setHeader('Cache-Control', 'no-store'); - res.setHeader('Pragma', 'no-cache'); - res.end(json); - }; + res.setHeader('Content-Type', 'application/json'); + res.setHeader('Cache-Control', 'no-store'); + res.setHeader('Pragma', 'no-cache'); + res.end(json); + }; - issue(client, profile, scope, issued); + issue(client, profile, scope, issued); }); }; }; diff --git a/lib/util.js b/lib/util.js index d63956a..c70c667 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1,10 +1,14 @@ 'use strict'; - +require('dotenv'); var request = require('request'); +var crypto = require('crypto'); + +exports.getFacebookProfile = function (fields, accessToken, cb) { + var appSecretProof = crypto.createHmac('sha256', process.env.FB_APP_SECRET).update(accessToken).digest('hex'); + var fieldsString = fields.join(); -exports.getFacebookProfile = function (accessToken, cb) { request({ - url: 'https://graph.facebook.com/me?access_token=' + accessToken, + url: 'https://graph.facebook.com/me?fields=' + fieldsString + '&access_token=' + accessToken + '&appsecret_proof=' + appSecretProof, json: true }, function (err, res, body) { diff --git a/package.json b/package.json index a22a51e..37cbcd6 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "test": "eslint lib/" }, "dependencies": { + "dotenv": "^4.0.0", "object-assign": "^2.0.0", "request": "^2.53.0" },