google-id-token-verifier
Version:
A small library to validate a google ID token for consuming it in node.js backend server.
172 lines (140 loc) • 5.71 kB
JavaScript
;
var _ = require('underscore');
var assert = require('assert');
var crypto = require('crypto');
var fs = require('fs');
var sinon = require('sinon');
var request = require('request');
var verifier = require('../lib/main');
var testEnvelope = require('./fixtures/envelope');
var testToken = require('./fixtures/idToken');
var testOAuthCerts = require('./fixtures/oauthcerts');
function makeFakeIdToken(payload, envelope) {
var privateKey = fs.readFileSync('./test/fixtures/private.pem', 'utf-8');
var data = new Buffer(JSON.stringify(envelope)).toString('base64') +
'.' + new Buffer(JSON.stringify(payload)).toString('base64');
var signer = crypto.createSign('sha256');
signer.update(data);
var signature = signer.sign(privateKey, 'base64');
data += '.' + signature;
return data;
}
function makeValidPayload(payload) {
var maxLifetimeSecs = 86400;
var now = new Date().getTime() / 1000;
var expiry = now + (maxLifetimeSecs / 2);
payload.iat = now;
payload.exp = expiry;
return payload;
}
describe('verifying google idToken', function () {
before(function (done) {
sinon
.stub(request, 'get')
.yields(null, { statusCode: 200, headers: {} }, testOAuthCerts);
done();
});
after(function (done) {
request.get.restore();
done();
});
it('should succeed to get tokenInfo with verifying', function (done) {
var payload = makeValidPayload(_.clone(testToken));
var envelope = _.clone(testEnvelope);
var data = makeFakeIdToken(payload, envelope);
verifier.verify(data, payload.aud, function (error, tokenInfo) {
assert.equal(_.isError(error), false);
assert.equal(tokenInfo.sub, payload.sub);
done();
});
});
it('should fail to load payload if iss is invalid', function (done) {
var payload = makeValidPayload(_.clone(testToken));
payload.iss = 'invalid.issuer.domain';
var envelope = _.clone(testEnvelope);
var data = makeFakeIdToken(payload, envelope);
verifier.verify(data, payload.aud, function (error, tokenInfo) {
assert.equal(_.isError(error), true);
assert.equal(error.message, 'Invalid idToken issuer');
assert.equal(_.isEmpty(tokenInfo), true);
done();
});
});
it('should fail to verify signature if jwk is invalid', function (done) {
var payload = makeValidPayload(_.clone(testToken));
var envelope = _.clone(testEnvelope);
envelope.kid = 'invalidKid';
var data = makeFakeIdToken(payload, envelope);
verifier.verify(data, payload.aud, function (error, tokenInfo) {
assert.equal(_.isError(error), true);
assert.equal(error.message, 'Cannot not found valid JWK');
assert.equal(_.isEmpty(tokenInfo), true);
done();
});
});
it('should fail to decode JWT if idToken is invalid', function (done) {
var payload = makeValidPayload(_.clone(testToken));
var envelope = _.clone(testEnvelope);
var data = makeFakeIdToken(payload, envelope);
data += '.invalidCode';
verifier.verify(data, payload.aud, function (error, tokenInfo) {
assert.equal(_.isError(error), true);
assert.equal(error.message, 'Invalid idToken');
assert.equal(_.isEmpty(tokenInfo), true);
done();
});
});
it('should fail to decode JWT if payload is invalid', function (done) {
var payload = makeValidPayload(_.clone(testToken));
var envelope = _.clone(testEnvelope);
var privateKey = fs.readFileSync('./test/fixtures/private.pem', 'utf-8');
var data = new Buffer(JSON.stringify(envelope)).toString('base64') +
'.' + new Buffer(JSON.stringify(payload) + 'invalidJson').toString('base64');
var signer = crypto.createSign('sha256');
signer.update(data);
var signature = signer.sign(privateKey, 'base64');
data += '.' + signature;
verifier.verify(data, payload.aud, function (error, tokenInfo) {
assert.equal(_.isError(error), true);
assert.equal(error.message, 'Invalid payload');
assert.equal(_.isEmpty(tokenInfo), true);
done();
});
});
it('should fail to verify signature if signature is invalid', function (done) {
var payload = makeValidPayload(_.clone(testToken));
var envelope = _.clone(testEnvelope);
var data = makeFakeIdToken(payload, envelope);
var invalidSignatureData = data.substring(0, data.lastIndexOf('.') + 1) + 'InvalidSignature';
verifier.verify(invalidSignatureData, payload.aud, function (error, tokenInfo) {
assert.equal(_.isError(error), true);
assert.equal(error.message, 'Invalid Signature');
assert.equal(_.isEmpty(tokenInfo), true);
done();
});
});
it('should fail to verify payload if audience is invalid', function (done) {
var payload = makeValidPayload(_.clone(testToken));
payload.aud = 'invalid.audience.domain';
var envelope = _.clone(testEnvelope);
var data = makeFakeIdToken(payload, envelope);
verifier.verify(data, testToken.aud, function (error, tokenInfo) {
assert.equal(_.isError(error), true);
assert.equal(error.message, 'Invalid idToken audience');
assert.equal(_.isEmpty(tokenInfo), true);
done();
});
});
it('should fail to verify payload if idToken is expired', function (done) {
var payload = makeValidPayload(_.clone(testToken));
payload.exp = new Date(0).getTime() / 1000;
var envelope = _.clone(testEnvelope);
var data = makeFakeIdToken(payload, envelope);
verifier.verify(data, payload.aud, function (error, tokenInfo) {
assert.equal(_.isError(error), true);
assert.equal(error.message, 'Expired idToken');
assert.equal(_.isEmpty(tokenInfo), true);
done();
});
});
});