From 23e18d5bf8fbc0fa727e5b838eeb7f44eca56bf4 Mon Sep 17 00:00:00 2001 From: Lars de Bruijn Date: Wed, 9 Dec 2015 13:50:12 +0100 Subject: [PATCH 1/6] Added scope check for the authorized scope by the user. This allows an error to be thrown when a required scope is not authorized --- lib/index.js | 5 +++-- lib/util.js | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/lib/index.js b/lib/index.js index 132117e..4ef400d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -20,6 +20,7 @@ module.exports = function (opts, issue) { var userProperty = opts.userProperty || 'user'; var separators = opts.scopeSeparator || ' '; + var requiredScopes = opts.requiredScopes || []; if (!Array.isArray(separators)) { separators = [ separators ]; @@ -43,10 +44,10 @@ module.exports = function (opts, issue) { 'Missing Facebook access token!', 'invalid_request')); } - getFacebookProfile(token, function (err, profile) { + getFacebookProfile(token, requiredScopes, function (err, profile) { if (err) { return next(new AuthorizationError( - 'Could not get Facebook profile using provided access token.', + err.message || 'Could not get Facebook profile using provided access token.', 'invalid_request' )); } diff --git a/lib/util.js b/lib/util.js index d63956a..fa62ae1 100644 --- a/lib/util.js +++ b/lib/util.js @@ -2,7 +2,7 @@ var request = require('request'); -exports.getFacebookProfile = function (accessToken, cb) { +exports.getFacebookProfile = function (accessToken, requiredScopes, cb) { request({ url: 'https://graph.facebook.com/me?access_token=' + accessToken, json: true @@ -17,6 +17,51 @@ exports.getFacebookProfile = function (accessToken, cb) { return cb(new Error(msg)); } - cb(null, body); + if (!requiredScopes) { + return cb(null, body); + } + + getFacebookPermissions(accessToken, function(err, scopes) { + var grantedScopes = []; + + for (var index in scopes) { + if (scopes.hasOwnProperty(index)) { + var scope = scopes[index]; + + if (scope.status === 'granted') { + grantedScopes.push(scope.permission); + } + } + } + + for (var index in requiredScopes) { + if (requiredScopes.hasOwnProperty(index)) { + var rScope = requiredScopes[index]; + if (grantedScopes.indexOf(rScope) === -1) { + var msg = 'Facebook profile authorization has insufficient scopes'; + return cb(new Error(msg)); + } + } + } + cb(null, body); + }); + }; + +getFacebookPermissions = function(accessToken, cb) { + request({ + url: 'https://graph.facebook.com/me/permissions?access_token=' + accessToken, + json: true + }, + function (err, res, body) { + if (err) { + return cb(err); + } + + if (body && body.error) { + var msg = body.error.message || 'Could not get Facebook profile permissions.'; + return cb(new Error(msg)); + } + + cb(null, body.data); }); -}; +} \ No newline at end of file From 13381b1ee2e77111441d68372d8fe66cca2f0ca2 Mon Sep 17 00:00:00 2001 From: Lars de Bruijn Date: Wed, 9 Dec 2015 13:55:27 +0100 Subject: [PATCH 2/6] Updated README.md with requiredScopes example --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index b577e5c..7cc6dfa 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,26 @@ server.exchange(oauth2orizeFacebook(function (client, profile, scope, cb) { })); ``` + +## Options + +**requiredScopes** + +Required scopes allows the exchange middleware to throw an error if the user does not allow a permission required for the application to work. +The requiredScopes are specified within the options (`opts`) object. + +``` +server.exchange( + oauth2orizeFacebook( + { + requiredScopes: ['scope1', 'scope2'] + }, + function (client, profile, scope, cb) { + // ... + } + ) +); +``` ## License MIT licensed. From 5e1fec81695b269530d36f454f459b1600583d45 Mon Sep 17 00:00:00 2001 From: Lars de Bruijn Date: Wed, 9 Dec 2015 13:56:22 +0100 Subject: [PATCH 3/6] JS code highlights in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7cc6dfa..aaf30da 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ server.exchange(oauth2orizeFacebook(function (client, profile, scope, cb) { Required scopes allows the exchange middleware to throw an error if the user does not allow a permission required for the application to work. The requiredScopes are specified within the options (`opts`) object. -``` +```js server.exchange( oauth2orizeFacebook( { From 3e24a80b135ef03f0f86e0a61e51f623523cc2c3 Mon Sep 17 00:00:00 2001 From: Lars de Bruijn Date: Wed, 9 Dec 2015 13:57:32 +0100 Subject: [PATCH 4/6] Formatting --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index aaf30da..9321012 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,8 @@ Required scopes allows the exchange middleware to throw an error if the user doe The requiredScopes are specified within the options (`opts`) object. ```js +// ... + server.exchange( oauth2orizeFacebook( { From ac03e898d0375d765d85adf31b2428f8af54a018 Mon Sep 17 00:00:00 2001 From: Lars de Bruijn Date: Wed, 9 Dec 2015 17:41:36 +0100 Subject: [PATCH 5/6] Realised a comparison would always return true, fixed that --- lib/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util.js b/lib/util.js index fa62ae1..0d533b9 100644 --- a/lib/util.js +++ b/lib/util.js @@ -17,7 +17,7 @@ exports.getFacebookProfile = function (accessToken, requiredScopes, cb) { return cb(new Error(msg)); } - if (!requiredScopes) { + if (requiredScopes.length === 0) { return cb(null, body); } From c011d3e02f4b7712f52e688474d39bebdfc08275 Mon Sep 17 00:00:00 2001 From: Lars de Bruijn Date: Wed, 9 Dec 2015 17:54:51 +0100 Subject: [PATCH 6/6] Bit more input validation --- lib/index.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/index.js b/lib/index.js index 4ef400d..bfdc9ea 100644 --- a/lib/index.js +++ b/lib/index.js @@ -22,6 +22,14 @@ module.exports = function (opts, issue) { var separators = opts.scopeSeparator || ' '; var requiredScopes = opts.requiredScopes || []; + if (!Array.isArray(requiredScopes)) { + if (typeof(requiredScopes) === 'string') { + requiredScopes = [ requiredScopes ]; + } else { + requiredScopes = []; + } + } + if (!Array.isArray(separators)) { separators = [ separators ]; }