google-id-token-verifier
Version:
A small library to validate a google ID token for consuming it in node.js backend server.
83 lines (68 loc) • 2.25 kB
JavaScript
;
var _ = require('underscore');
var certCache = require('./certCache');
var crypto = require('crypto');
var getPem = require('rsa-pem-from-mod-exp');
function decodeBase64WithUriEncoding(encodedText) {
return new Buffer(encodedText, 'base64').toString('utf8');
}
function unescapedString(escapedString) {
escapedString += new Array(5 - escapedString.length % 4).join('=');
return escapedString.replace(/\-/g, '+').replace(/_/g, '/');
}
function decodeJWT(idToken) {
var token = idToken.split('.');
if (token.length !== 3) {
throw new Error('Invalid idToken');
}
try {
var headerSegment = JSON.parse(decodeBase64WithUriEncoding(token[0]));
var payloadSegment = JSON.parse(decodeBase64WithUriEncoding(token[1]));
var signature = unescapedString(token[2]);
return {
dataToSign: [token[0], token[1]].join('.'),
header: headerSegment,
payload: payloadSegment,
signature: signature
};
} catch (e) {
throw new Error('Invalid payload');
}
}
function verifySignature(jwt, keys) {
var kid = jwt.header.kid;
if (_.isUndefined(kid) || !_.contains(_.pluck(keys, 'kid'), kid)) {
throw new Error('Cannot not found valid JWK');
}
var key = _.findWhere(keys, {kid: kid});
var pem = getPem(key.n, key.e);
var verifier = crypto.createVerify('sha256');
verifier.update(jwt.dataToSign);
if (!verifier.verify(pem, jwt.signature, 'base64')) {
throw new Error('Invalid Signature');
}
}
function verifyPayload(payload, audience) {
var now = new Date();
if (!_.contains(['accounts.google.com', 'https://accounts.google.com'], payload.iss)) {
throw new Error('Invalid idToken issuer');
}
if (payload.aud !== audience) {
throw new Error('Invalid idToken audience');
}
if (now > new Date(payload.exp * 1000)) {
throw new Error('Expired idToken');
}
}
exports.verify = function (idToken, audience, callback) {
certCache.global.getFederatedGoogleCerts(function (err, keys) {
try {
var decodedJWT = decodeJWT(idToken);
verifySignature(decodedJWT, keys.keys);
verifyPayload(decodedJWT.payload, audience);
callback(null, decodedJWT.payload);
} catch (e) {
callback(e, null);
}
});
};