From 9ffd93a730aeae6a5f945c5e73848522a914e850 Mon Sep 17 00:00:00 2001 From: Guilherme Felipe da Silva Date: Fri, 3 Nov 2017 23:00:28 +0000 Subject: [PATCH 1/5] Fixed vulnerability and added fields option --- lib/index.js | 109 ++++++++++++++++++++++++++++----------------------- lib/util.js | 10 +++-- 2 files changed, 68 insertions(+), 51 deletions(-) diff --git a/lib/index.js b/lib/index.js index 132117e..69a1ad7 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 (!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) { From 83721b41fa808b8dc01e9aae28ef727aa6a74d03 Mon Sep 17 00:00:00 2001 From: Guilherme Felipe da Silva Date: Fri, 3 Nov 2017 23:06:38 +0000 Subject: [PATCH 2/5] Changed README to explain how to provide FB_APP_SECRET --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b577e5c..a49fb60 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,21 @@ npm i oauth2orize-facebook -S ## Usage +You must provide your app secret using + +```sh +export FB_APP_SECRET={your Facebook app secret} +``` + +or adding FB_APP_SECRET to your .env file. + ```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'; From 064449010f1f6b63ac94ebcda095e59b33944e7f Mon Sep 17 00:00:00 2001 From: Guilherme Felipe da Silva Date: Fri, 3 Nov 2017 23:18:21 +0000 Subject: [PATCH 3/5] Added dotenv package to the package.json file --- README.md | 5 +++-- package.json | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a49fb60..dd4aa3e 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,14 @@ npm i oauth2orize-facebook -S ## Usage -You must provide your app secret using +Before using this package, you should enable the 'Require App Secret' option in the advanced settings of your Facebook app. +Then, you must provide the app secret by exporting it ```sh export FB_APP_SECRET={your Facebook app secret} ``` -or adding FB_APP_SECRET to your .env file. +or by adding FB_APP_SECRET to your .env file. ```js var oauth2orize = require('oauth2orize'); 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" }, From 85c5e63db9f526c9a1ee6eec600e380ac3a13fae Mon Sep 17 00:00:00 2001 From: Guilherme Felipe da Silva Date: Fri, 3 Nov 2017 21:23:32 -0200 Subject: [PATCH 4/5] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dd4aa3e..63a6412 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,15 @@ 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. -Then, you must provide the app secret by exporting it +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 to your .env file. +or by adding FB_APP_SECRET={your Facebook app secret} to your .env file. + +Then, you can have fun ```js var oauth2orize = require('oauth2orize'); From 75258ca1a5e735e29133c80115b44014a08674ed Mon Sep 17 00:00:00 2001 From: Guilherme Felipe da Silva Date: Wed, 22 Nov 2017 09:55:48 -0200 Subject: [PATCH 5/5] As fields are optional, we need to check if it is present --- lib/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/index.js b/lib/index.js index 69a1ad7..872b7dd 100644 --- a/lib/index.js +++ b/lib/index.js @@ -18,7 +18,7 @@ module.exports = function (opts, fields, issue) { fields = null; } - if (!Array.isArray(fields)) { + 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'); }