Skip to content

Commit 4cc1a85

Browse files
Merge pull request #80 from Mastercard/chore/up-test-coverage
Chore/up test coverage
2 parents fcb727b + 2dff48c commit 4cc1a85

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

test/signature/jws.test.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
const { expect } = require('chai');
2+
const sinon = require('sinon');
3+
const { KJUR } = require('jsrsasign');
4+
const { jwsSign, jwsVerify } = require('../../src/signature/jws');
5+
6+
describe('jws', () => {
7+
let sandbox;
8+
9+
const payload = { foo: 'bar' };
10+
const algo = 'PS256';
11+
const kid = 'test-kid';
12+
const privateKey = 'test-private-key';
13+
const publicKey = 'test-public-key';
14+
15+
const encodeHeader = (header) => Buffer.from(JSON.stringify(header), 'utf-8').toString('base64url');
16+
const encodePayload = (data) => Buffer.from(JSON.stringify(data), 'utf-8').toString('base64url');
17+
18+
beforeEach(() => {
19+
sandbox = sinon.createSandbox();
20+
});
21+
22+
afterEach(() => {
23+
sandbox.restore();
24+
});
25+
26+
describe('jwsSign', () => {
27+
it('builds header with kid, alg and iat then strips payload', () => {
28+
const nowSeconds = 1_700_000_000;
29+
const intDateStub = sandbox.stub(KJUR.jws.IntDate, 'get').withArgs('now').returns(nowSeconds);
30+
const signStub = sandbox.stub(KJUR.jws.JWS, 'sign').callsFake((_algo, header, signPayload, key) => {
31+
expect(_algo).to.be.null;
32+
expect(header).to.deep.equal({ alg: algo, kid, crit: ['iat'], iat: nowSeconds.toString() });
33+
expect(signPayload).to.equal(payload);
34+
expect(key).to.equal(privateKey);
35+
return 'header.payload.signature';
36+
});
37+
38+
const result = jwsSign(payload, kid, privateKey, algo);
39+
40+
expect(result).to.equal('header..signature');
41+
expect(intDateStub.calledOnce).to.be.true;
42+
expect(signStub.calledOnce).to.be.true;
43+
});
44+
});
45+
46+
describe('jwsVerify', () => {
47+
const baseHeader = { crit: ['iat'], iat: 1_700_000_000, alg: algo };
48+
const payloadB64 = encodePayload(payload);
49+
50+
const buildJws = (headerOverrides = {}, signature = 'sig') => {
51+
const header = { ...baseHeader, ...headerOverrides };
52+
const headerB64 = encodeHeader(header);
53+
return { jws: `${headerB64}..${signature}`, header, headerB64 };
54+
};
55+
56+
it('reconstructs the full JWS and verifies with allowed algorithm', () => {
57+
const clock = sandbox.useFakeTimers(new Date('2024-01-01T00:00:00Z'));
58+
const currentIat = Math.floor(clock.now / 1000);
59+
const { jws, headerB64 } = buildJws({ iat: currentIat });
60+
const verifyStub = sandbox.stub(KJUR.jws.JWS, 'verify').returns(true);
61+
62+
const result = jwsVerify(jws, payload, publicKey, 60, [algo]);
63+
64+
expect(result).to.be.true;
65+
expect(verifyStub.calledOnceWith(`${headerB64}.${payloadB64}.sig`, publicKey, [algo])).to.be.true;
66+
});
67+
68+
it('throws when JWS is malformed', () => {
69+
expect(() => jwsVerify('onlytwo.parts', payload, publicKey, 60, [algo])).to.throw('Invalid JWS header');
70+
});
71+
72+
it('throws when algorithm is not permitted', () => {
73+
const { jws } = buildJws({ alg: 'RS256' });
74+
75+
expect(() => jwsVerify(jws, payload, publicKey, 60, [algo])).to.throw('Unsupported Signature verification algorithm');
76+
});
77+
78+
it('throws when crit header is invalid', () => {
79+
const { jws } = buildJws({ crit: ['sub'] });
80+
81+
expect(() => jwsVerify(jws, payload, publicKey, 60, [algo])).to.throw('Header crit of JWS Signature must contain only iat');
82+
});
83+
84+
it('throws when iat is missing', () => {
85+
const { jws } = buildJws({ iat: undefined });
86+
87+
expect(() => jwsVerify(jws, payload, publicKey, 60, [algo])).to.throw('Missing header iat in JWS signature');
88+
});
89+
90+
it('throws when iat is not an integer', () => {
91+
const { jws } = buildJws({ iat: 'not-a-number' });
92+
93+
expect(() => jwsVerify(jws, payload, publicKey, 60, [algo])).to.throw('Header iat of JWS Signature must be a valid timestamp');
94+
});
95+
96+
it('throws when the signature is expired', () => {
97+
const clock = sandbox.useFakeTimers(new Date('2024-01-01T00:00:00Z'));
98+
const staleIat = Math.floor(clock.now / 1000) - 120;
99+
const { jws } = buildJws({ iat: staleIat });
100+
101+
expect(() => jwsVerify(jws, payload, publicKey, 60, [algo])).to.throw('The signature has expired');
102+
});
103+
});
104+
});

0 commit comments

Comments
 (0)