From 9c23b5e561a644ff360de9f173099e384ab2963d Mon Sep 17 00:00:00 2001 From: mockitor Date: Sun, 4 Dec 2016 16:29:36 +0300 Subject: [PATCH 1/4] Firebase authentication provider is added. --- package.json | 1 + src/authDataManager/firebase.js | 43 +++++++++++++++++++++++++++++++++ src/authDataManager/index.js | 4 ++- 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/authDataManager/firebase.js diff --git a/package.json b/package.json index 0f5aa4e18c..57c05163d6 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "commander": "2.9.0", "deepcopy": "0.6.3", "express": "4.14.0", + "firebase-admin": "^4.0.3", "intersect": "1.0.1", "lodash": "4.17.2", "lru-cache": "4.0.2", diff --git a/src/authDataManager/firebase.js b/src/authDataManager/firebase.js new file mode 100644 index 0000000000..e536af8c1b --- /dev/null +++ b/src/authDataManager/firebase.js @@ -0,0 +1,43 @@ +// Helper functions for accessing the qq Graph API. +var Parse = require('parse/node').Parse; +var admin = require("firebase-admin"); + + +var firebaseServiceAccount = process.env.FIREBASE_SERVICE_ACCOUNT; +if (firebaseServiceAccount != null) { + admin.initializeApp({ + credential: admin.credential.cert(require(firebaseServiceAccount)) + }); +} + + +// Returns a promise that fulfills iff this user id is valid. +function validateAuthData(authData) { + if (firebaseServiceAccount == null || firebaseServiceAccount == "") { + throw new Parse.Error(Parse.Error.FILE_READ_ERROR, "Firebase service account file not exists"); + } + + return admin.auth().verifyIdToken(authData.access_token) + .then(function (decodedToken) { + console.log(decodedToken) + if (uid == null || uid == "") { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "Firebase user not found"); + } + return; + }).catch(function (error) { + console.log(error) + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, error.message); + }); + +} + +// Returns a promise that fulfills if this app id is valid. +function validateAppId() { + return Promise.resolve(); +} + + +module.exports = { + validateAppId, + validateAuthData +}; diff --git a/src/authDataManager/index.js b/src/authDataManager/index.js index 5fe0f32b60..36466514f1 100755 --- a/src/authDataManager/index.js +++ b/src/authDataManager/index.js @@ -13,6 +13,7 @@ let vkontakte = require("./vkontakte"); let qq = require("./qq"); let wechat = require("./wechat"); let weibo = require("./weibo"); +let firebase = require("./firebase"); let anonymous = { validateAuthData: () => { @@ -39,7 +40,8 @@ let providers = { vkontakte, qq, wechat, - weibo + weibo, + firebase } module.exports = function(oauthOptions = {}, enableAnonymousUsers = true) { From e6f0cc235fbb12f36532131cdb659dc7d332bb8a Mon Sep 17 00:00:00 2001 From: mockitor Date: Sun, 4 Dec 2016 16:47:41 +0300 Subject: [PATCH 2/4] fixed NPE --- src/authDataManager/firebase.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/authDataManager/firebase.js b/src/authDataManager/firebase.js index e536af8c1b..714e5809d7 100644 --- a/src/authDataManager/firebase.js +++ b/src/authDataManager/firebase.js @@ -19,8 +19,7 @@ function validateAuthData(authData) { return admin.auth().verifyIdToken(authData.access_token) .then(function (decodedToken) { - console.log(decodedToken) - if (uid == null || uid == "") { + if (decodedToken == null || decodedToken.uid == null || decodedToken.uid == "") { throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "Firebase user not found"); } return; From 4c95f552bde7c4b175e80ebb4e805bcecfbfbaf0 Mon Sep 17 00:00:00 2001 From: mockitor Date: Sun, 4 Dec 2016 17:16:28 +0300 Subject: [PATCH 3/4] fixed lint errors --- src/authDataManager/firebase.js | 39 ++++++++++++++++----------------- src/authDataManager/index.js | 3 ++- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/authDataManager/firebase.js b/src/authDataManager/firebase.js index 714e5809d7..35ecb5d4bd 100644 --- a/src/authDataManager/firebase.js +++ b/src/authDataManager/firebase.js @@ -5,38 +5,37 @@ var admin = require("firebase-admin"); var firebaseServiceAccount = process.env.FIREBASE_SERVICE_ACCOUNT; if (firebaseServiceAccount != null) { - admin.initializeApp({ - credential: admin.credential.cert(require(firebaseServiceAccount)) - }); + admin.initializeApp({ + credential: admin.credential.cert(require(firebaseServiceAccount)) + }); } // Returns a promise that fulfills iff this user id is valid. function validateAuthData(authData) { - if (firebaseServiceAccount == null || firebaseServiceAccount == "") { - throw new Parse.Error(Parse.Error.FILE_READ_ERROR, "Firebase service account file not exists"); - } - - return admin.auth().verifyIdToken(authData.access_token) - .then(function (decodedToken) { - if (decodedToken == null || decodedToken.uid == null || decodedToken.uid == "") { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "Firebase user not found"); - } - return; - }).catch(function (error) { - console.log(error) - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, error.message); - }); + if (firebaseServiceAccount == null || firebaseServiceAccount == "") { + throw new Parse.Error(Parse.Error.FILE_READ_ERROR, "Firebase service account file not exists"); + } + + return admin.auth().verifyIdToken(authData.access_token) + .then(function (decodedToken) { + if (decodedToken == null || decodedToken.uid == null || decodedToken.uid == "") { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "Firebase user not found"); + } + return; + }).catch(function (error) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, error.message); + }); } // Returns a promise that fulfills if this app id is valid. function validateAppId() { - return Promise.resolve(); + return Promise.resolve(); } module.exports = { - validateAppId, - validateAuthData + validateAppId, + validateAuthData }; diff --git a/src/authDataManager/index.js b/src/authDataManager/index.js index 36466514f1..94d81e7b79 100755 --- a/src/authDataManager/index.js +++ b/src/authDataManager/index.js @@ -15,6 +15,7 @@ let wechat = require("./wechat"); let weibo = require("./weibo"); let firebase = require("./firebase"); + let anonymous = { validateAuthData: () => { return Promise.resolve(); @@ -41,7 +42,7 @@ let providers = { qq, wechat, weibo, - firebase + firebase } module.exports = function(oauthOptions = {}, enableAnonymousUsers = true) { From 314d5e8aa161ee1536d1dcfd460c592fc3a36feb Mon Sep 17 00:00:00 2001 From: mockitor Date: Wed, 7 Dec 2016 03:53:40 +0300 Subject: [PATCH 4/4] Removed firebase-admin-sdk dependency. Firebase id token verification is implemented with third part jsonwebtoken library. --- package.json | 1 + src/authDataManager/firebase.js | 62 ++++++++++++++++++++------------- src/authDataManager/index.js | 1 - 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index 57c05163d6..553f2374ca 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "express": "4.14.0", "firebase-admin": "^4.0.3", "intersect": "1.0.1", + "jsonwebtoken": "7.2.0", "lodash": "4.17.2", "lru-cache": "4.0.2", "mime": "1.3.4", diff --git a/src/authDataManager/firebase.js b/src/authDataManager/firebase.js index 35ecb5d4bd..117302898f 100644 --- a/src/authDataManager/firebase.js +++ b/src/authDataManager/firebase.js @@ -1,32 +1,45 @@ -// Helper functions for accessing the qq Graph API. -var Parse = require('parse/node').Parse; -var admin = require("firebase-admin"); +// Firebase authentication provider +var https = require('https'); +var jwt = require("jsonwebtoken"); +// Returns a promise that fulfills iff this user id is valid. +function validateAuthData(authData) { + return request().then(function (response) { + var publicKey = response[Object.keys(response)[0]] + try { + var decodedToken = jwt.verify(authData.access_token, publicKey); + console.log(authData) + + if (decodedToken == null || decodedToken.id == null || decodedToken.id == "") { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Google auth is invalid for this user.'); + } + resolve(decodedToken); + } catch (error) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "Token validation error"); + } + + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Google auth is invalid for this user.'); -var firebaseServiceAccount = process.env.FIREBASE_SERVICE_ACCOUNT; -if (firebaseServiceAccount != null) { - admin.initializeApp({ - credential: admin.credential.cert(require(firebaseServiceAccount)) }); } - -// Returns a promise that fulfills iff this user id is valid. -function validateAuthData(authData) { - if (firebaseServiceAccount == null || firebaseServiceAccount == "") { - throw new Parse.Error(Parse.Error.FILE_READ_ERROR, "Firebase service account file not exists"); - } - - return admin.auth().verifyIdToken(authData.access_token) - .then(function (decodedToken) { - if (decodedToken == null || decodedToken.uid == null || decodedToken.uid == "") { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "Firebase user not found"); - } - return; - }).catch(function (error) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, error.message); +// A promisey wrapper for api requests +function request() { + return new Promise(function (resolve, reject) { + var googlePublicKeyUrl = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com"; + https.get(googlePublicKeyUrl, function (res) { + var data = ''; + res.on('data', function (chunk) { + data += chunk; }); - + res.on('end', function () { + data = JSON.parse(data); + resolve(data); + }); + }).on('error', function () { + reject('Failed to validate this access token with Google.'); + }); + }); } // Returns a promise that fulfills if this app id is valid. @@ -34,8 +47,7 @@ function validateAppId() { return Promise.resolve(); } - module.exports = { validateAppId, validateAuthData -}; +}; \ No newline at end of file diff --git a/src/authDataManager/index.js b/src/authDataManager/index.js index 94d81e7b79..58bccc321f 100755 --- a/src/authDataManager/index.js +++ b/src/authDataManager/index.js @@ -15,7 +15,6 @@ let wechat = require("./wechat"); let weibo = require("./weibo"); let firebase = require("./firebase"); - let anonymous = { validateAuthData: () => { return Promise.resolve();