2019-06-19 16:05:09 -05:00
|
|
|
const Parse = require('parse/node').Parse;
|
|
|
|
|
const httpsRequest = require('./httpsRequest');
|
|
|
|
|
const NodeRSA = require('node-rsa');
|
|
|
|
|
const jwt = require('jsonwebtoken');
|
|
|
|
|
|
|
|
|
|
const TOKEN_ISSUER = 'https://appleid.apple.com';
|
|
|
|
|
|
2019-07-25 10:20:28 -07:00
|
|
|
let currentKey;
|
|
|
|
|
|
2019-06-19 16:05:09 -05:00
|
|
|
const getApplePublicKey = async () => {
|
2019-07-25 10:20:28 -07:00
|
|
|
let data;
|
|
|
|
|
try {
|
|
|
|
|
data = await httpsRequest.get('https://appleid.apple.com/auth/keys');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
if (currentKey) {
|
|
|
|
|
return currentKey;
|
|
|
|
|
}
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-19 16:05:09 -05:00
|
|
|
const key = data.keys[0];
|
|
|
|
|
|
|
|
|
|
const pubKey = new NodeRSA();
|
|
|
|
|
pubKey.importKey(
|
|
|
|
|
{ n: Buffer.from(key.n, 'base64'), e: Buffer.from(key.e, 'base64') },
|
|
|
|
|
'components-public'
|
|
|
|
|
);
|
2019-07-25 10:20:28 -07:00
|
|
|
currentKey = pubKey.exportKey(['public']);
|
|
|
|
|
return currentKey;
|
2019-06-19 16:05:09 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const verifyIdToken = async (token, clientID) => {
|
|
|
|
|
if (!token) {
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
Parse.Error.OBJECT_NOT_FOUND,
|
2019-07-03 16:28:29 -05:00
|
|
|
'id token is invalid for this user.'
|
2019-06-19 16:05:09 -05:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
const applePublicKey = await getApplePublicKey();
|
|
|
|
|
const jwtClaims = jwt.verify(token, applePublicKey, { algorithms: 'RS256' });
|
|
|
|
|
|
|
|
|
|
if (jwtClaims.iss !== TOKEN_ISSUER) {
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
Parse.Error.OBJECT_NOT_FOUND,
|
2019-07-03 16:28:29 -05:00
|
|
|
`id token not issued by correct OpenID provider - expected: ${TOKEN_ISSUER} | from: ${jwtClaims.iss}`
|
2019-06-19 16:05:09 -05:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (clientID !== undefined && jwtClaims.aud !== clientID) {
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
Parse.Error.OBJECT_NOT_FOUND,
|
|
|
|
|
`jwt aud parameter does not include this client - is: ${jwtClaims.aud} | expected: ${clientID}`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return jwtClaims;
|
|
|
|
|
};
|
|
|
|
|
|
2019-07-03 16:28:29 -05:00
|
|
|
// Returns a promise that fulfills if this id token is valid
|
2019-06-19 16:05:09 -05:00
|
|
|
function validateAuthData(authData, options = {}) {
|
2019-07-03 16:28:29 -05:00
|
|
|
return verifyIdToken(authData.id, options.client_id);
|
2019-06-19 16:05:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a promise that fulfills if this app id is valid.
|
|
|
|
|
function validateAppId() {
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
2019-07-03 16:28:29 -05:00
|
|
|
validateAppId,
|
|
|
|
|
validateAuthData,
|
2019-06-19 16:05:09 -05:00
|
|
|
};
|