Added tests for request integrity token validation

This commit is contained in:
Sam Lord 2024-08-22 22:07:06 +01:00 committed by Sam Lord
parent ebc87002ce
commit 23c0882019
2 changed files with 74 additions and 4 deletions

View File

@ -7,7 +7,7 @@ class RequestIntegrityTokenProvider {
/**
* @param {object} options
* @param {string} options.themeSecret
* @param {number} options.tokenDuration
* @param {number} options.tokenDuration - in milliseconds
*/
constructor(options) {
this.#themeSecret = options.themeSecret;
@ -37,12 +37,12 @@ class RequestIntegrityTokenProvider {
return false;
}
const nonce = parts[0];
const timestamp = parseInt(parts[1], 10);
const timestamp = parseInt(parts[0], 10);
const nonce = parts[1];
const hmacDigest = parts[2];
const hmac = crypto.createHmac('sha256', this.#themeSecret);
hmac.update(`${nonce}:${timestamp.toString()}`);
hmac.update(`${timestamp.toString()}:${nonce}`);
const expectedHmac = hmac.digest('hex');
if (expectedHmac !== hmacDigest) {

View File

@ -0,0 +1,70 @@
const sinon = require('sinon');
const should = require('should');
const RequestIntegrityTokenProvider = require('../../../../../core/server/services/members/RequestIntegrityTokenProvider');
const tokenProvider = new RequestIntegrityTokenProvider({
themeSecret: 'test',
tokenDuration: 100
});
describe('RequestIntegrityTokenProvider', function () {
beforeEach(function () {
sinon.useFakeTimers(new Date('2021-01-01'));
});
afterEach(function () {
sinon.restore();
});
describe('create', function () {
it('should create a HMAC digest from the secret', function () {
const token = tokenProvider.create();
token.should.be.a.String();
token.split(':').should.be.an.Array().with.lengthOf(3);
const [timestamp, nonce, digest] = token.split(':');
timestamp.should.equal((new Date('2021-01-01').valueOf() + 100).toString());
nonce.should.match(/[0-9a-f]{16}/);
digest.should.be.a.String().with.lengthOf(64);
});
});
describe('validate', function () {
it('should verify a HMAC digest from the secret', function () {
const token = tokenProvider.create();
const result = tokenProvider.validate(token);
result.should.be.true();
});
it('should fail to verify an expired token', function () {
const token = tokenProvider.create();
sinon.clock.tick(101);
const result = tokenProvider.validate(token);
result.should.be.false();
});
it('should fail to verify a malformed token', function () {
const token = 'invalid_token';
const result = tokenProvider.validate(token);
result.should.be.false();
});
it('should fail to verify a token with an invalid signature', function () {
const token = tokenProvider.create();
const [timestamp, nonce] = token.split(':');
const invalidDigest = 'a'.repeat(64); // Create an invalid digest
const invalidToken = `${timestamp}:${nonce}:${invalidDigest}`;
const result = tokenProvider.validate(invalidToken);
result.should.be.false();
});
});
});