diff --git a/packages/google-oauth/google_client.js b/packages/google-oauth/google_client.js index c7b54dccfb..a45c080011 100644 --- a/packages/google-oauth/google_client.js +++ b/packages/google-oauth/google_client.js @@ -1,5 +1,13 @@ var Google = require("./namespace.js"); +var ILLEGAL_PARAMETERS = { + 'response_type': 1, + 'client_id': 1, + 'scope': 1, + 'redirect_uri': 1, + 'state': 1 +}; + // Request Google credentials for the user // @param options {optional} // @param credentialRequestCompleteCallback {Function} Callback function to call on @@ -24,24 +32,26 @@ Google.requestCredential = function (options, credentialRequestCompleteCallback) var credentialToken = Random.secret(); // we need the email scope to get user id from google. - var requiredScope = ['email']; - var scope = ['profile']; - if (options.requestPermissions) - scope = options.requestPermissions; - scope = _.union(scope, requiredScope); + var requiredScopes = { 'email': 1 }; + var scopes = options.requestPermissions || ['profile']; + scopes.forEach(function (scope) { + requiredScopes[scope] = 1; + }); + scopes = Object.keys(requiredScopes); var loginUrlParameters = {}; if (config.loginUrlParameters){ - _.extend(loginUrlParameters, config.loginUrlParameters) + Object.assign(loginUrlParameters, config.loginUrlParameters); } if (options.loginUrlParameters){ - _.extend(loginUrlParameters, options.loginUrlParameters) + Object.assign(loginUrlParameters, options.loginUrlParameters); } - var ILLEGAL_PARAMETERS = ['response_type', 'client_id', 'scope', 'redirect_uri', 'state']; - // validate options keys - _.each(_.keys(loginUrlParameters), function (key) { - if (_.contains(ILLEGAL_PARAMETERS, key)) + + // validate options keys + Object.keys(loginUrlParameters).forEach(function (key) { + if (ILLEGAL_PARAMETERS.hasOwnProperty(key)) { throw new Error("Google.requestCredential: Invalid loginUrlParameter: " + key); + } }); // backwards compatible options @@ -60,16 +70,17 @@ Google.requestCredential = function (options, credentialRequestCompleteCallback) var loginStyle = OAuth._loginStyle('google', config, options); // https://developers.google.com/accounts/docs/OAuth2WebServer#formingtheurl - _.extend(loginUrlParameters, { + Object.assign(loginUrlParameters, { "response_type": "code", "client_id": config.clientId, - "scope": scope.join(' '), // space delimited + "scope": scopes.join(' '), // space delimited "redirect_uri": OAuth._redirectUri('google', config), "state": OAuth._stateParam(loginStyle, credentialToken, options.redirectUrl) }); var loginUrl = 'https://accounts.google.com/o/oauth2/auth?' + - _.map(loginUrlParameters, function(value, param){ - return encodeURIComponent(param) + '=' + encodeURIComponent(value); + Object.keys(loginUrlParameters).map(function (param) { + return encodeURIComponent(param) + '=' + + encodeURIComponent(loginUrlParameters[param]); }).join("&"); OAuth.launchLogin({ diff --git a/packages/google-oauth/google_server.js b/packages/google-oauth/google_server.js index 6b9b3f00e5..c430bd5637 100644 --- a/packages/google-oauth/google_server.js +++ b/packages/google-oauth/google_server.js @@ -1,5 +1,6 @@ var Google = require("./namespace.js"); var Accounts = require("meteor/accounts-base").Accounts; +var hasOwn = Object.prototype.hasOwnProperty; // https://developers.google.com/accounts/docs/OAuth2Login#userinfocall Google.whitelistedFields = ['id', 'email', 'verified_email', 'name', 'given_name', @@ -20,8 +21,14 @@ function getServiceData(query) { scope: scopes }; - var fields = _.pick(identity, Google.whitelistedFields); - _.extend(serviceData, fields); + var fields = Object.create(null); + Google.whitelistedFields.forEach(function (name) { + if (hasOwn.call(identity, name)) { + fields[name] = identity[name]; + } + }); + + Object.assign(serviceData, fields); // only set the token in serviceData if it's there. this ensures // that we don't lose old ones (since we only get this on the first @@ -40,15 +47,16 @@ Accounts.registerLoginHandler(function (request) { return; } - const res = getServiceData({code: request.serverAuthCode}); - - return Accounts.updateOrCreateUserFromExternalService("google", _.extend({ + return Accounts.updateOrCreateUserFromExternalService("google", { id: request.userId, idToken: request.idToken, accessToken: request.accessToken, email: request.email, - picture: request.imageUrl - }, res.serviceData)); + picture: request.imageUrl, + ...getServiceData({ + code: request.serverAuthCode + }).serviceData, + }); }); OAuth.registerService('google', 2, null, getServiceData); @@ -73,8 +81,10 @@ var getTokens = function (query) { grant_type: 'authorization_code' }}); } catch (err) { - throw _.extend(new Error("Failed to complete OAuth handshake with Google. " + err.message), - {response: err.response}); + throw Object.assign( + new Error("Failed to complete OAuth handshake with Google. " + err.message), + { response: err.response } + ); } if (response.data.error) { // if the http response was a json object with an error attribute @@ -95,8 +105,10 @@ var getIdentity = function (accessToken) { "https://www.googleapis.com/oauth2/v1/userinfo", {params: {access_token: accessToken}}).data; } catch (err) { - throw _.extend(new Error("Failed to fetch identity from Google. " + err.message), - {response: err.response}); + throw Object.assign( + new Error("Failed to fetch identity from Google. " + err.message), + { response: err.response } + ); } }; @@ -106,8 +118,10 @@ var getScopes = function (accessToken) { "https://www.googleapis.com/oauth2/v1/tokeninfo", {params: {access_token: accessToken}}).data.scope.split(' '); } catch (err) { - throw _.extend(new Error("Failed to fetch tokeninfo from Google. " + err.message), - {response: err.response}); + throw Object.assign( + new Error("Failed to fetch tokeninfo from Google. " + err.message), + { response: err.response } + ); } }; diff --git a/packages/google-oauth/google_sign-in.js b/packages/google-oauth/google_sign-in.js index 4d21403264..3ba3a54ea8 100644 --- a/packages/google-oauth/google_sign-in.js +++ b/packages/google-oauth/google_sign-in.js @@ -59,12 +59,14 @@ exports.signIn = Google.signIn = function (options, callback) { function getScopes(options) { // we need the email scope to get user id from google. - var requiredScopes = ['email']; - var scopes = ['profile']; - if (options && options.requestPermissions) { - scopes = options.requestPermissions; - } - return _.union(scopes, requiredScopes); + var requiredScopes = { 'email': 1 }; + var scopes = options.requestPermissions || ['profile']; + + scopes.forEach(function (scope) { + requiredScopes[scope] = 1; + }); + + return Object.keys(requiredScopes); } exports.signOut = Google.signOut = function () { diff --git a/packages/google-oauth/package.js b/packages/google-oauth/package.js index d977bb6d16..4432aff495 100644 --- a/packages/google-oauth/package.js +++ b/packages/google-oauth/package.js @@ -14,12 +14,11 @@ Cordova.depends({ }); Package.onUse(function(api) { - api.use("modules"); - api.use("promise"); + api.use("ecmascript"); api.use('oauth2', ['client', 'server']); api.use('oauth', ['client', 'server']); api.use('http', ['server']); - api.use(['underscore', 'service-configuration'], ['client', 'server']); + api.use('service-configuration'); api.use('random', 'client'); api.addFiles('google_server.js', 'server');