Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Auth Method Added. #4484

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
90e24b9
AuthAdapter added
kenglou Dec 12, 2017
4fc7e2c
fix underscore module
kenglou Dec 12, 2017
065f2a3
remove the checking for provider
kenglou Dec 12, 2017
934b7b2
Merge branch 'master' into master
kenglou Dec 13, 2017
6173067
checking to make sure the provider exists!
kenglou Dec 13, 2017
5feca46
Merge branch 'master' of https://github.com/kenglou/parse-server
kenglou Dec 13, 2017
f29099b
change check provider method
kenglou Dec 13, 2017
4f9cf55
Fix GPGames Error
kenglou Dec 15, 2017
7de2a97
Take Away checking for Provider Name
kenglou Dec 15, 2017
bfd7aec
add rest.spec for Google Play Games.
kenglou Dec 18, 2017
867dbc5
Fixing the Auth Coverage Test
kenglou Dec 18, 2017
143c94e
trying to fulfil the test case of myoauth
kenglou Dec 18, 2017
fdfeaa9
commit this for pass the test
kenglou Dec 18, 2017
18d6e58
Added another test case oauth method...
kenglou Dec 18, 2017
10cccc9
Test Again for request another build pass for CI
kenglou Dec 18, 2017
76b2be1
Test to Cover Test case of unknown provider
kenglou Dec 18, 2017
faa596a
Test to Cover Test case of unknown provider
kenglou Dec 18, 2017
6026c75
add check test for authentication handler not appear on index.
kenglou Dec 18, 2017
0fee82b
remove the done that doesn't has any function
kenglou Dec 18, 2017
cf74430
comply the test function.
kenglou Dec 18, 2017
f66c4b6
for the test coverage.
kenglou Dec 18, 2017
9596b8c
add test case for game center
kenglou Dec 18, 2017
4173c84
forget to pass in the validor as verify is the function of it.
kenglou Dec 18, 2017
9925f3d
again.
kenglou Dec 18, 2017
0fbeed1
test case
kenglou Dec 18, 2017
ae1f02c
second submission for test
kenglou Dec 18, 2017
4848161
Second Chance for building test case for this.
kenglou Dec 18, 2017
f47e438
added Xiao Mi Login Authentication Method.
kenglou Dec 21, 2017
0e39a0e
edit xiao mi to make it reliable.
kenglou Dec 22, 2017
c6974cb
Merge branch 'master' into master
kenglou Jan 5, 2018
a570d72
Make index.js to follow standard
kenglou Jan 15, 2018
2f954c1
Merge branch 'master' into master
kenglou Jan 15, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion spec/AuthenticationAdapters.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var authenticationLoader = require('../src/Adapters/Auth');
var path = require('path');

describe('AuthenticationProviders', function() {
["facebook", "github", "instagram", "google", "linkedin", "meetup", "twitter", "janrainengage", "janraincapture", "vkontakte"].map(function(providerName){
["facebook", "github", "instagram", "google", "linkedin", "meetup", "twitter", "janrainengage", "janraincapture", "vkontakte", "gpgames", "gcenter","xiaomi"].map(function(providerName){
it("Should validate structure of " + providerName, (done) => {
var provider = require("../src/Adapters/Auth/" + providerName);
jequal(typeof provider.validateAuthData, "function");
Expand Down Expand Up @@ -345,4 +345,34 @@ describe('AuthenticationProviders', function() {
expect(appIds).toEqual(['a', 'b']);
expect(providerOptions).toEqual(options.custom);
});

it('should fail to load adapter if provider is not available on the list', () => {
const authenticationHandler = authenticationLoader();
validateAuthenticationHandler(authenticationHandler);
const validator = authenticationHandler.getValidatorForProvider('unknown');
expect(validator).toBeUndefined();
});

it('properly verify a game center identity', () => {
const authenticationHandler = authenticationLoader({
gcenter: path.resolve('./src/Adapters/Auth/gcenter.js')
});

validateAuthenticationHandler(authenticationHandler);
const validator = authenticationHandler.getValidatorForProvider('gcenter');
var identity = {
playerId: "G:1958377822",
publicKeyUrl: "https://static.gc.apple.com/public-key/gc-prod-3.cer",
timestamp: "1513067500622",
signature: "BYX5fJ59vVnj/3pdAfsfzkdSHG5kqPJ/gnhagaFJYwAYRlhR9P672sZk0N/cZoCabzTZM+AVMc6W+xwpVVSj/qxgfZi4ADvwKY7m8q8ugdw3Aec97w8zL3i5F5wKLr+HPzR6/OW4YsMQjtuumpWQCq+9bDro9JibiCVKm+x/MwvIgHS3gdi6XVD+JeBMLbd5bJk/wUmcjE9uWMoXRiY4WCREtlIy47eGWYSFQ2kinIKo4YRzpSK95E9jYEB0nOjwg1ExycvMBPDrxoxpk+maIkqPZ3FS6vESLnhq1/gojwECEmObRxv+a1ZRvzt2fwCMfjBzZVv867r+mpF7H/JrXA==",
salt: "WN97Bw==",
bundleId: "com.appxplore.apptest"
};
validateValidator(validator);
validator(identity, function (err, token)
{
expect(err).toBeNull();
expect(token).not.toBeNull();
});
});
});
129 changes: 129 additions & 0 deletions src/Adapters/Auth/gcenter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
'use strict';

// Helper functions for accessing the google API.
//var Parse = require('parse/node').Parse;
var crypto = require('crypto');
var request = require('request');
var url = require('url');

// Returns a promise that fulfills if this user id is valid.
function validateAuthData(authData)
{
dplewis marked this conversation as resolved.
Show resolved Hide resolved
return new Promise(function (resolve, reject)
{
var identity = {
publicKeyUrl: authData.pKeyUrl,
timestamp: authData.timeStamp,
signature: authData.sig,
salt: authData.salt,
playerId: authData.id,
bundleId: authData.bid
};

return verify(identity, function (err, token)
{
if(err)
return reject('Failed to validate this access token with Game Center.');
else
return resolve(token);
});
});
}

// Returns a promise that fulfills if this app id is valid.
function validateAppId() {
return Promise.resolve();
}

function verifyPublicKeyUrl(publicKeyUrl) {
var parsedUrl = url.parse(publicKeyUrl);
if (parsedUrl.protocol !== 'https:') {
return false;
}

var hostnameParts = parsedUrl.hostname.split('.');
var domain = hostnameParts[hostnameParts.length - 2] + "." + hostnameParts[hostnameParts.length - 1];
if (domain !== 'apple.com') {
return false;
}

return true;
}

function convertX509CertToPEM(X509Cert) {
var pemPreFix = '-----BEGIN CERTIFICATE-----\n';
var pemPostFix = '-----END CERTIFICATE-----';

var base64 = X509Cert.toString('base64');
var certBody = base64.match(new RegExp('.{0,64}', 'g')).join('\n');

return pemPreFix + certBody + pemPostFix;
}

function getAppleCertificate(publicKeyUrl, callback) {
if (!verifyPublicKeyUrl(publicKeyUrl)) {
callback(new Error('Invalid publicKeyUrl'), null);
return;
}

var options = {
uri: publicKeyUrl,
encoding: null
};
request.get(options, function (error, response, body) {
if (!error && response.statusCode === 200) {
var cert = convertX509CertToPEM(body);
callback(null, cert);
} else {
callback(error, null);
}
});
}

/* jslint bitwise:true */
function convertTimestampToBigEndian(timestamp) {
// The timestamp parameter in Big-Endian UInt-64 format
var buffer = new Buffer(8);
buffer.fill(0);

var high = ~~(timestamp / 0xffffffff); // jshint ignore:line
var low = timestamp % (0xffffffff + 0x1); // jshint ignore:line

buffer.writeUInt32BE(parseInt(high, 10), 0);
buffer.writeUInt32BE(parseInt(low, 10), 4);

return buffer;
}
/* jslint bitwise:false */

function verifySignature(publicKey, idToken) {
var verifier = crypto.createVerify('sha256');
verifier.update(idToken.playerId, 'utf8');
verifier.update(idToken.bundleId, 'utf8');
verifier.update(convertTimestampToBigEndian(idToken.timestamp));
verifier.update(idToken.salt, 'base64');

if (!verifier.verify(publicKey, idToken.signature, 'base64')) {
throw new Error('Invalid Signature');
}
}

function verify(idToken, callback) {
getAppleCertificate(idToken.publicKeyUrl, function (err, publicKey) {
if (!err) {
try {
verifySignature(publicKey, idToken);
callback(null, idToken);
} catch (e) {
callback(e, null);
}
} else {
callback(err, null);
}
});
}

module.exports = {
validateAppId: validateAppId,
validateAuthData: validateAuthData
};
94 changes: 94 additions & 0 deletions src/Adapters/Auth/gpgames.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
'use strict';

// Helper functions for accessing the google API.
var https = require('https');
var Parse = require('parse/node').Parse;
var request = require('request');

// Returns a promise that fulfills if this user id is valid.
function validateAuthData(authData, authOptions)
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, keep the curly braces up on the same line. It's inconsistent throughout this file as well if you can amend that.

var postUrl = {
url: 'https://www.googleapis.com/oauth2/v3/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
form: {
'client_id': authOptions.client_id,
'client_secret': authOptions.client_secret,
'code': authData.access_token,
'grant_type': 'authorization_code'
}
};
return exchangeAccessToken(postUrl).then((authRes)=>
{
if(authRes.error)
{
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, authRes.error);
}
else
{
return requestHere("https://www.googleapis.com/games/v1/players/" + authData.id + "?access_token=" + authRes.access_token).then(response => {
if (response && (response.playerId == authData.id))
return;
else
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Google auth is invalid for this user.');
});
}
}).catch(error=>{
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, error);
});
}

// Returns a promise that fulfills if this app id is valid.
function validateAppId() {
return Promise.resolve();
}

function exchangeAccessToken(postOptions)
{
return new Promise(function (resolve, reject) {
request(postOptions, function (error, response, body)
{
if (!error && response.statusCode == 200)
{
try {
body = JSON.parse(body);
} catch (e) {
return reject(e);
}
resolve(body);
}
else
reject("Fail to Exchange Access Token for GPGames");
});
});
}

// A promisey wrapper for api requests
function requestHere(path) {
return new Promise(function (resolve, reject) {
https.get(path, function (res) {
var data = '';
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
try {
data = JSON.parse(data);
} catch (e) {
return reject(e);
}
resolve(data);
});
}).on('error', function () {
reject('Failed to validate this access token with Google.');
});
});
}

module.exports = {
validateAppId: validateAppId,
validateAuthData: validateAuthData
};
Loading