123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- 'use strict';
- var Parse = require('parse/node').Parse;
- const https = require('https');
- const jwt = require('jsonwebtoken');
- const authUtils = require('./utils');
- const TOKEN_ISSUER = 'accounts.google.com';
- const HTTPS_TOKEN_ISSUER = 'https://accounts.google.com';
- let cache = {};
- function getGoogleKeyByKeyId(keyId) {
- if (cache[keyId] && cache.expiresAt > new Date()) {
- return cache[keyId];
- }
- return new Promise((resolve, reject) => {
- https.get(`https://www.googleapis.com/oauth2/v3/certs`, res => {
- let data = '';
- res.on('data', chunk => {
- data += chunk.toString('utf8');
- });
- res.on('end', () => {
- const {
- keys
- } = JSON.parse(data);
- const pems = keys.reduce((pems, {
- n: modulus,
- e: exposant,
- kid
- }) => Object.assign(pems, {
- [kid]: rsaPublicKeyToPEM(modulus, exposant)
- }), {});
- if (res.headers['cache-control']) {
- var expire = res.headers['cache-control'].match(/max-age=([0-9]+)/);
- if (expire) {
- cache = Object.assign({}, pems, {
- expiresAt: new Date(new Date().getTime() + Number(expire[1]) * 1000)
- });
- }
- }
- resolve(pems[keyId]);
- });
- }).on('error', reject);
- });
- }
- async function verifyIdToken({
- id_token: token,
- id
- }, {
- clientId
- }) {
- if (!token) {
- throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token is invalid for this user.`);
- }
- const {
- kid: keyId,
- alg: algorithm
- } = authUtils.getHeaderFromToken(token);
- let jwtClaims;
- const googleKey = await getGoogleKeyByKeyId(keyId);
- try {
- jwtClaims = jwt.verify(token, googleKey, {
- algorithms: algorithm,
- audience: clientId
- });
- } catch (exception) {
- const message = exception.message;
- throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `${message}`);
- }
- if (jwtClaims.iss !== TOKEN_ISSUER && jwtClaims.iss !== HTTPS_TOKEN_ISSUER) {
- throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token not issued by correct provider - expected: ${TOKEN_ISSUER} or ${HTTPS_TOKEN_ISSUER} | from: ${jwtClaims.iss}`);
- }
- if (jwtClaims.sub !== id) {
- throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `auth data is invalid for this user.`);
- }
- if (clientId && jwtClaims.aud !== clientId) {
- throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token not authorized for this clientId.`);
- }
- return jwtClaims;
- }
- function validateAuthData(authData, options = {}) {
- return verifyIdToken(authData, options);
- }
- function validateAppId() {
- return Promise.resolve();
- }
- module.exports = {
- validateAppId: validateAppId,
- validateAuthData: validateAuthData
- };
- function rsaPublicKeyToPEM(modulusB64, exponentB64) {
- const modulus = new Buffer(modulusB64, 'base64');
- const exponent = new Buffer(exponentB64, 'base64');
- const modulusHex = prepadSigned(modulus.toString('hex'));
- const exponentHex = prepadSigned(exponent.toString('hex'));
- const modlen = modulusHex.length / 2;
- const explen = exponentHex.length / 2;
- const encodedModlen = encodeLengthHex(modlen);
- const encodedExplen = encodeLengthHex(explen);
- const encodedPubkey = '30' + encodeLengthHex(modlen + explen + encodedModlen.length / 2 + encodedExplen.length / 2 + 2) + '02' + encodedModlen + modulusHex + '02' + encodedExplen + exponentHex;
- const der = new Buffer(encodedPubkey, 'hex').toString('base64');
- let pem = '-----BEGIN RSA PUBLIC KEY-----\n';
- pem += `${der.match(/.{1,64}/g).join('\n')}`;
- pem += '\n-----END RSA PUBLIC KEY-----\n';
- return pem;
- }
- function prepadSigned(hexStr) {
- const msb = hexStr[0];
- if (msb < '0' || msb > '7') {
- return `00${hexStr}`;
- }
- return hexStr;
- }
- function toHex(number) {
- const nstr = number.toString(16);
- if (nstr.length % 2) {
- return `0${nstr}`;
- }
- return nstr;
- }
- function encodeLengthHex(n) {
- if (n <= 127) {
- return toHex(n);
- }
- const nHex = toHex(n);
- const lengthOfLengthByte = 128 + nHex.length / 2;
- return toHex(lengthOfLengthByte) + nHex;
- }
|