google.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. 'use strict';
  2. // Helper functions for accessing the google API.
  3. var Parse = require('parse/node').Parse;
  4. const https = require('https');
  5. const jwt = require('jsonwebtoken');
  6. const authUtils = require('./utils');
  7. const TOKEN_ISSUER = 'accounts.google.com';
  8. const HTTPS_TOKEN_ISSUER = 'https://accounts.google.com';
  9. let cache = {};
  10. // Retrieve Google Signin Keys (with cache control)
  11. function getGoogleKeyByKeyId(keyId) {
  12. if (cache[keyId] && cache.expiresAt > new Date()) {
  13. return cache[keyId];
  14. }
  15. return new Promise((resolve, reject) => {
  16. https.get(`https://www.googleapis.com/oauth2/v3/certs`, res => {
  17. let data = '';
  18. res.on('data', chunk => {
  19. data += chunk.toString('utf8');
  20. });
  21. res.on('end', () => {
  22. const {
  23. keys
  24. } = JSON.parse(data);
  25. const pems = keys.reduce((pems, {
  26. n: modulus,
  27. e: exposant,
  28. kid
  29. }) => Object.assign(pems, {
  30. [kid]: rsaPublicKeyToPEM(modulus, exposant)
  31. }), {});
  32. if (res.headers['cache-control']) {
  33. var expire = res.headers['cache-control'].match(/max-age=([0-9]+)/);
  34. if (expire) {
  35. cache = Object.assign({}, pems, {
  36. expiresAt: new Date(new Date().getTime() + Number(expire[1]) * 1000)
  37. });
  38. }
  39. }
  40. resolve(pems[keyId]);
  41. });
  42. }).on('error', reject);
  43. });
  44. }
  45. async function verifyIdToken({
  46. id_token: token,
  47. id
  48. }, {
  49. clientId
  50. }) {
  51. if (!token) {
  52. throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token is invalid for this user.`);
  53. }
  54. const {
  55. kid: keyId,
  56. alg: algorithm
  57. } = authUtils.getHeaderFromToken(token);
  58. let jwtClaims;
  59. const googleKey = await getGoogleKeyByKeyId(keyId);
  60. try {
  61. jwtClaims = jwt.verify(token, googleKey, {
  62. algorithms: algorithm,
  63. audience: clientId
  64. });
  65. } catch (exception) {
  66. const message = exception.message;
  67. throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `${message}`);
  68. }
  69. if (jwtClaims.iss !== TOKEN_ISSUER && jwtClaims.iss !== HTTPS_TOKEN_ISSUER) {
  70. 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}`);
  71. }
  72. if (jwtClaims.sub !== id) {
  73. throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `auth data is invalid for this user.`);
  74. }
  75. if (clientId && jwtClaims.aud !== clientId) {
  76. throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token not authorized for this clientId.`);
  77. }
  78. return jwtClaims;
  79. }
  80. // Returns a promise that fulfills if this user id is valid.
  81. function validateAuthData(authData, options = {}) {
  82. return verifyIdToken(authData, options);
  83. }
  84. // Returns a promise that fulfills if this app id is valid.
  85. function validateAppId() {
  86. return Promise.resolve();
  87. }
  88. module.exports = {
  89. validateAppId: validateAppId,
  90. validateAuthData: validateAuthData
  91. };
  92. // Helpers functions to convert the RSA certs to PEM (from jwks-rsa)
  93. function rsaPublicKeyToPEM(modulusB64, exponentB64) {
  94. const modulus = new Buffer(modulusB64, 'base64');
  95. const exponent = new Buffer(exponentB64, 'base64');
  96. const modulusHex = prepadSigned(modulus.toString('hex'));
  97. const exponentHex = prepadSigned(exponent.toString('hex'));
  98. const modlen = modulusHex.length / 2;
  99. const explen = exponentHex.length / 2;
  100. const encodedModlen = encodeLengthHex(modlen);
  101. const encodedExplen = encodeLengthHex(explen);
  102. const encodedPubkey = '30' + encodeLengthHex(modlen + explen + encodedModlen.length / 2 + encodedExplen.length / 2 + 2) + '02' + encodedModlen + modulusHex + '02' + encodedExplen + exponentHex;
  103. const der = new Buffer(encodedPubkey, 'hex').toString('base64');
  104. let pem = '-----BEGIN RSA PUBLIC KEY-----\n';
  105. pem += `${der.match(/.{1,64}/g).join('\n')}`;
  106. pem += '\n-----END RSA PUBLIC KEY-----\n';
  107. return pem;
  108. }
  109. function prepadSigned(hexStr) {
  110. const msb = hexStr[0];
  111. if (msb < '0' || msb > '7') {
  112. return `00${hexStr}`;
  113. }
  114. return hexStr;
  115. }
  116. function toHex(number) {
  117. const nstr = number.toString(16);
  118. if (nstr.length % 2) {
  119. return `0${nstr}`;
  120. }
  121. return nstr;
  122. }
  123. function encodeLengthHex(n) {
  124. if (n <= 127) {
  125. return toHex(n);
  126. }
  127. const nHex = toHex(n);
  128. const lengthOfLengthByte = 128 + nHex.length / 2;
  129. return toHex(lengthOfLengthByte) + nHex;
  130. }
  131. //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJQYXJzZSIsInJlcXVpcmUiLCJodHRwcyIsImp3dCIsImF1dGhVdGlscyIsIlRPS0VOX0lTU1VFUiIsIkhUVFBTX1RPS0VOX0lTU1VFUiIsImNhY2hlIiwiZ2V0R29vZ2xlS2V5QnlLZXlJZCIsImtleUlkIiwiZXhwaXJlc0F0IiwiRGF0ZSIsIlByb21pc2UiLCJyZXNvbHZlIiwicmVqZWN0IiwiZ2V0IiwicmVzIiwiZGF0YSIsIm9uIiwiY2h1bmsiLCJ0b1N0cmluZyIsImtleXMiLCJKU09OIiwicGFyc2UiLCJwZW1zIiwicmVkdWNlIiwibiIsIm1vZHVsdXMiLCJlIiwiZXhwb3NhbnQiLCJraWQiLCJPYmplY3QiLCJhc3NpZ24iLCJyc2FQdWJsaWNLZXlUb1BFTSIsImhlYWRlcnMiLCJleHBpcmUiLCJtYXRjaCIsImdldFRpbWUiLCJOdW1iZXIiLCJ2ZXJpZnlJZFRva2VuIiwiaWRfdG9rZW4iLCJ0b2tlbiIsImlkIiwiY2xpZW50SWQiLCJFcnJvciIsIk9CSkVDVF9OT1RfRk9VTkQiLCJhbGciLCJhbGdvcml0aG0iLCJnZXRIZWFkZXJGcm9tVG9rZW4iLCJqd3RDbGFpbXMiLCJnb29nbGVLZXkiLCJ2ZXJpZnkiLCJhbGdvcml0aG1zIiwiYXVkaWVuY2UiLCJleGNlcHRpb24iLCJtZXNzYWdlIiwiaXNzIiwic3ViIiwiYXVkIiwidmFsaWRhdGVBdXRoRGF0YSIsImF1dGhEYXRhIiwib3B0aW9ucyIsInZhbGlkYXRlQXBwSWQiLCJtb2R1bGUiLCJleHBvcnRzIiwibW9kdWx1c0I2NCIsImV4cG9uZW50QjY0IiwiQnVmZmVyIiwiZXhwb25lbnQiLCJtb2R1bHVzSGV4IiwicHJlcGFkU2lnbmVkIiwiZXhwb25lbnRIZXgiLCJtb2RsZW4iLCJsZW5ndGgiLCJleHBsZW4iLCJlbmNvZGVkTW9kbGVuIiwiZW5jb2RlTGVuZ3RoSGV4IiwiZW5jb2RlZEV4cGxlbiIsImVuY29kZWRQdWJrZXkiLCJkZXIiLCJwZW0iLCJqb2luIiwiaGV4U3RyIiwibXNiIiwidG9IZXgiLCJudW1iZXIiLCJuc3RyIiwibkhleCIsImxlbmd0aE9mTGVuZ3RoQnl0ZSJdLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9BZGFwdGVycy9BdXRoL2dvb2dsZS5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbi8vIEhlbHBlciBmdW5jdGlvbnMgZm9yIGFjY2Vzc2luZyB0aGUgZ29vZ2xlIEFQSS5cbnZhciBQYXJzZSA9IHJlcXVpcmUoJ3BhcnNlL25vZGUnKS5QYXJzZTtcblxuY29uc3QgaHR0cHMgPSByZXF1aXJlKCdodHRwcycpO1xuY29uc3Qgand0ID0gcmVxdWlyZSgnanNvbndlYnRva2VuJyk7XG5jb25zdCBhdXRoVXRpbHMgPSByZXF1aXJlKCcuL3V0aWxzJyk7XG5cbmNvbnN0IFRPS0VOX0lTU1VFUiA9ICdhY2NvdW50cy5nb29nbGUuY29tJztcbmNvbnN0IEhUVFBTX1RPS0VOX0lTU1VFUiA9ICdodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20nO1xuXG5sZXQgY2FjaGUgPSB7fTtcblxuLy8gUmV0cmlldmUgR29vZ2xlIFNpZ25pbiBLZXlzICh3aXRoIGNhY2hlIGNvbnRyb2wpXG5mdW5jdGlvbiBnZXRHb29nbGVLZXlCeUtleUlkKGtleUlkKSB7XG4gIGlmIChjYWNoZVtrZXlJZF0gJiYgY2FjaGUuZXhwaXJlc0F0ID4gbmV3IERhdGUoKSkge1xuICAgIHJldHVybiBjYWNoZVtrZXlJZF07XG4gIH1cblxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIGh0dHBzXG4gICAgICAuZ2V0KGBodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9vYXV0aDIvdjMvY2VydHNgLCByZXMgPT4ge1xuICAgICAgICBsZXQgZGF0YSA9ICcnO1xuICAgICAgICByZXMub24oJ2RhdGEnLCBjaHVuayA9PiB7XG4gICAgICAgICAgZGF0YSArPSBjaHVuay50b1N0cmluZygndXRmOCcpO1xuICAgICAgICB9KTtcbiAgICAgICAgcmVzLm9uKCdlbmQnLCAoKSA9PiB7XG4gICAgICAgICAgY29uc3QgeyBrZXlzIH0gPSBKU09OLnBhcnNlKGRhdGEpO1xuICAgICAgICAgIGNvbnN0IHBlbXMgPSBrZXlzLnJlZHVjZShcbiAgICAgICAgICAgIChwZW1zLCB7IG46IG1vZHVsdXMsIGU6IGV4cG9zYW50LCBraWQgfSkgPT5cbiAgICAgICAgICAgICAgT2JqZWN0LmFzc2lnbihwZW1zLCB7XG4gICAgICAgICAgICAgICAgW2tpZF06IHJzYVB1YmxpY0tleVRvUEVNKG1vZHVsdXMsIGV4cG9zYW50KSxcbiAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICB7fVxuICAgICAgICAgICk7XG5cbiAgICAgICAgICBpZiAocmVzLmhlYWRlcnNbJ2NhY2hlLWNvbnRyb2wnXSkge1xuICAgICAgICAgICAgdmFyIGV4cGlyZSA9IHJlcy5oZWFkZXJzWydjYWNoZS1jb250cm9sJ10ubWF0Y2goL21heC1hZ2U9KFswLTldKykvKTtcblxuICAgICAgICAgICAgaWYgKGV4cGlyZSkge1xuICAgICAgICAgICAgICBjYWNoZSA9IE9iamVjdC5hc3NpZ24oe30sIHBlbXMsIHtcbiAgICAgICAgICAgICAgICBleHBpcmVzQXQ6IG5ldyBEYXRlKG5ldyBEYXRlKCkuZ2V0VGltZSgpICsgTnVtYmVyKGV4cGlyZVsxXSkgKiAxMDAwKSxcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgcmVzb2x2ZShwZW1zW2tleUlkXSk7XG4gICAgICAgIH0pO1xuICAgICAgfSlcbiAgICAgIC5vbignZXJyb3InLCByZWplY3QpO1xuICB9KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gdmVyaWZ5SWRUb2tlbih7IGlkX3Rva2VuOiB0b2tlbiwgaWQgfSwgeyBjbGllbnRJZCB9KSB7XG4gIGlmICghdG9rZW4pIHtcbiAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCwgYGlkIHRva2VuIGlzIGludmFsaWQgZm9yIHRoaXMgdXNlci5gKTtcbiAgfVxuXG4gIGNvbnN0IHsga2lkOiBrZXlJZCwgYWxnOiBhbGdvcml0aG0gfSA9IGF1dGhVdGlscy5nZXRIZWFkZXJGcm9tVG9rZW4odG9rZW4pO1xuICBsZXQgand0Q2xhaW1zO1xuICBjb25zdCBnb29nbGVLZXkgPSBhd2FpdCBnZXRHb29nbGVLZXlCeUtleUlkKGtleUlkKTtcblxuICB0cnkge1xuICAgIGp3dENsYWltcyA9IGp3dC52ZXJpZnkodG9rZW4sIGdvb2dsZUtleSwge1xuICAgICAgYWxnb3JpdGhtczogYWxnb3JpdGhtLFxuICAgICAgYXVkaWVuY2U6IGNsaWVudElkLFxuICAgIH0pO1xuICB9IGNhdGNoIChleGNlcHRpb24pIHtcbiAgICBjb25zdCBtZXNzYWdlID0gZXhjZXB0aW9uLm1lc3NhZ2U7XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsIGAke21lc3NhZ2V9YCk7XG4gIH1cblxuICBpZiAoand0Q2xhaW1zLmlzcyAhPT0gVE9LRU5fSVNTVUVSICYmIGp3dENsYWltcy5pc3MgIT09IEhUVFBTX1RPS0VOX0lTU1VFUikge1xuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihcbiAgICAgIFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsXG4gICAgICBgaWQgdG9rZW4gbm90IGlzc3VlZCBieSBjb3JyZWN0IHByb3ZpZGVyIC0gZXhwZWN0ZWQ6ICR7VE9LRU5fSVNTVUVSfSBvciAke0hUVFBTX1RPS0VOX0lTU1VFUn0gfCBmcm9tOiAke2p3dENsYWltcy5pc3N9YFxuICAgICk7XG4gIH1cblxuICBpZiAoand0Q2xhaW1zLnN1YiAhPT0gaWQpIHtcbiAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCwgYGF1dGggZGF0YSBpcyBpbnZhbGlkIGZvciB0aGlzIHVzZXIuYCk7XG4gIH1cblxuICBpZiAoY2xpZW50SWQgJiYgand0Q2xhaW1zLmF1ZCAhPT0gY2xpZW50SWQpIHtcbiAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoXG4gICAgICBQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELFxuICAgICAgYGlkIHRva2VuIG5vdCBhdXRob3JpemVkIGZvciB0aGlzIGNsaWVudElkLmBcbiAgICApO1xuICB9XG5cbiAgcmV0dXJuIGp3dENsYWltcztcbn1cblxuLy8gUmV0dXJucyBhIHByb21pc2UgdGhhdCBmdWxmaWxscyBpZiB0aGlzIHVzZXIgaWQgaXMgdmFsaWQuXG5mdW5jdGlvbiB2YWxpZGF0ZUF1dGhEYXRhKGF1dGhEYXRhLCBvcHRpb25zID0ge30pIHtcbiAgcmV0dXJuIHZlcmlmeUlkVG9rZW4oYXV0aERhdGEsIG9wdGlvbnMpO1xufVxuXG4vLyBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IGZ1bGZpbGxzIGlmIHRoaXMgYXBwIGlkIGlzIHZhbGlkLlxuZnVuY3Rpb24gdmFsaWRhdGVBcHBJZCgpIHtcbiAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgdmFsaWRhdGVBcHBJZDogdmFsaWRhdGVBcHBJZCxcbiAgdmFsaWRhdGVBdXRoRGF0YTogdmFsaWRhdGVBdXRoRGF0YSxcbn07XG5cbi8vIEhlbHBlcnMgZnVuY3Rpb25zIHRvIGNvbnZlcnQgdGhlIFJTQSBjZXJ0cyB0byBQRU0gKGZyb20gandrcy1yc2EpXG5mdW5jdGlvbiByc2FQdWJsaWNLZXlUb1BFTShtb2R1bHVzQjY0LCBleHBvbmVudEI2NCkge1xuICBjb25zdCBtb2R1bHVzID0gbmV3IEJ1ZmZlcihtb2R1bHVzQjY0LCAnYmFzZTY0Jyk7XG4gIGNvbnN0IGV4cG9uZW50ID0gbmV3IEJ1ZmZlcihleHBvbmVudEI2NCwgJ2Jhc2U2NCcpO1xuICBjb25zdCBtb2R1bHVzSGV4ID0gcHJlcGFkU2lnbmVkKG1vZHVsdXMudG9TdHJpbmcoJ2hleCcpKTtcbiAgY29uc3QgZXhwb25lbnRIZXggPSBwcmVwYWRTaWduZWQoZXhwb25lbnQudG9TdHJpbmcoJ2hleCcpKTtcbiAgY29uc3QgbW9kbGVuID0gbW9kdWx1c0hleC5sZW5ndGggLyAyO1xuICBjb25zdCBleHBsZW4gPSBleHBvbmVudEhleC5sZW5ndGggLyAyO1xuXG4gIGNvbnN0IGVuY29kZWRNb2RsZW4gPSBlbmNvZGVMZW5ndGhIZXgobW9kbGVuKTtcbiAgY29uc3QgZW5jb2RlZEV4cGxlbiA9IGVuY29kZUxlbmd0aEhleChleHBsZW4pO1xuICBjb25zdCBlbmNvZGVkUHVia2V5ID1cbiAgICAnMzAnICtcbiAgICBlbmNvZGVMZW5ndGhIZXgobW9kbGVuICsgZXhwbGVuICsgZW5jb2RlZE1vZGxlbi5sZW5ndGggLyAyICsgZW5jb2RlZEV4cGxlbi5sZW5ndGggLyAyICsgMikgK1xuICAgICcwMicgK1xuICAgIGVuY29kZWRNb2RsZW4gK1xuICAgIG1vZHVsdXNIZXggK1xuICAgICcwMicgK1xuICAgIGVuY29kZWRFeHBsZW4gK1xuICAgIGV4cG9uZW50SGV4O1xuXG4gIGNvbnN0IGRlciA9IG5ldyBCdWZmZXIoZW5jb2RlZFB1YmtleSwgJ2hleCcpLnRvU3RyaW5nKCdiYXNlNjQnKTtcblxuICBsZXQgcGVtID0gJy0tLS0tQkVHSU4gUlNBIFBVQkxJQyBLRVktLS0tLVxcbic7XG4gIHBlbSArPSBgJHtkZXIubWF0Y2goLy57MSw2NH0vZykuam9pbignXFxuJyl9YDtcbiAgcGVtICs9ICdcXG4tLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tXFxuJztcbiAgcmV0dXJuIHBlbTtcbn1cblxuZnVuY3Rpb24gcHJlcGFkU2lnbmVkKGhleFN0cikge1xuICBjb25zdCBtc2IgPSBoZXhTdHJbMF07XG4gIGlmIChtc2IgPCAnMCcgfHwgbXNiID4gJzcnKSB7XG4gICAgcmV0dXJuIGAwMCR7aGV4U3RyfWA7XG4gIH1cbiAgcmV0dXJuIGhleFN0cjtcbn1cblxuZnVuY3Rpb24gdG9IZXgobnVtYmVyKSB7XG4gIGNvbnN0IG5zdHIgPSBudW1iZXIudG9TdHJpbmcoMTYpO1xuICBpZiAobnN0ci5sZW5ndGggJSAyKSB7XG4gICAgcmV0dXJuIGAwJHtuc3RyfWA7XG4gIH1cbiAgcmV0dXJuIG5zdHI7XG59XG5cbmZ1bmN0aW9uIGVuY29kZUxlbmd0aEhleChuKSB7XG4gIGlmIChuIDw9IDEyNykge1xuICAgIHJldHVybiB0b0hleChuKTtcbiAgfVxuICBjb25zdCBuSGV4ID0gdG9IZXgobik7XG4gIGNvbnN0IGxlbmd0aE9mTGVuZ3RoQnl0ZSA9IDEyOCArIG5IZXgubGVuZ3RoIC8gMjtcbiAgcmV0dXJuIHRvSGV4KGxlbmd0aE9mTGVuZ3RoQnl0ZSkgKyBuSGV4O1xufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxZQUFZOztBQUVaO0FBQ0EsSUFBSUEsS0FBSyxHQUFHQyxPQUFPLENBQUMsWUFBWSxDQUFDLENBQUNELEtBQUs7QUFFdkMsTUFBTUUsS0FBSyxHQUFHRCxPQUFPLENBQUMsT0FBTyxDQUFDO0FBQzlCLE1BQU1FLEdBQUcsR0FBR0YsT0FBTyxDQUFDLGNBQWMsQ0FBQztBQUNuQyxNQUFNRyxTQUFTLEdBQUdILE9BQU8sQ0FBQyxTQUFTLENBQUM7QUFFcEMsTUFBTUksWUFBWSxHQUFHLHFCQUFxQjtBQUMxQyxNQUFNQyxrQkFBa0IsR0FBRyw2QkFBNkI7QUFFeEQsSUFBSUMsS0FBSyxHQUFHLENBQUMsQ0FBQzs7QUFFZDtBQUNBLFNBQVNDLG1CQUFtQkEsQ0FBQ0MsS0FBSyxFQUFFO0VBQ2xDLElBQUlGLEtBQUssQ0FBQ0UsS0FBSyxDQUFDLElBQUlGLEtBQUssQ0FBQ0csU0FBUyxHQUFHLElBQUlDLElBQUksQ0FBQyxDQUFDLEVBQUU7SUFDaEQsT0FBT0osS0FBSyxDQUFDRSxLQUFLLENBQUM7RUFDckI7RUFFQSxPQUFPLElBQUlHLE9BQU8sQ0FBQyxDQUFDQyxPQUFPLEVBQUVDLE1BQU0sS0FBSztJQUN0Q1osS0FBSyxDQUNGYSxHQUFHLENBQUMsNENBQTRDLEVBQUVDLEdBQUcsSUFBSTtNQUN4RCxJQUFJQyxJQUFJLEdBQUcsRUFBRTtNQUNiRCxHQUFHLENBQUNFLEVB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