Skip to content

Commit 9b1ba42

Browse files
committed
Create jws.test.js
1 parent fcb727b commit 9b1ba42

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

test/signature/jws.test.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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 { jws, headerB64 } = buildJws();
58+
const verifyStub = sandbox.stub(KJUR.jws.JWS, 'verify').returns(true);
59+
60+
const result = jwsVerify(jws, payload, publicKey, 60, [algo]);
61+
62+
expect(result).to.be.true;
63+
expect(verifyStub.calledOnceWith(`${headerB64}.${payloadB64}.sig`, publicKey, [algo])).to.be.true;
64+
});
65+
66+
it('throws when JWS is malformed', () => {
67+
expect(() => jwsVerify('too.few.parts', payload, publicKey, 60, [algo])).to.throw('Invalid JWS header');
68+
});
69+
70+
it('throws when algorithm is not permitted', () => {
71+
const { jws } = buildJws({ alg: 'RS256' });
72+
73+
expect(() => jwsVerify(jws, payload, publicKey, 60, [algo])).to.throw('Unsupported Signature verification algorithm');
74+
});
75+
76+
it('throws when crit header is invalid', () => {
77+
const { jws } = buildJws({ crit: ['sub'] });
78+
79+
expect(() => jwsVerify(jws, payload, publicKey, 60, [algo])).to.throw('Header crit of JWS Signature must contain only iat');
80+
});
81+
82+
it('throws when iat is missing', () => {
83+
const { jws } = buildJws({ iat: undefined });
84+
85+
expect(() => jwsVerify(jws, payload, publicKey, 60, [algo])).to.throw('Missing header iat in JWS signature');
86+
});
87+
88+
it('throws when iat is not an integer', () => {
89+
const { jws } = buildJws({ iat: 'not-a-number' });
90+
91+
expect(() => jwsVerify(jws, payload, publicKey, 60, [algo])).to.throw('Header iat of JWS Signature must be a valid timestamp');
92+
});
93+
94+
it('throws when the signature is expired', () => {
95+
const clock = sandbox.useFakeTimers(new Date('2024-01-01T00:00:00Z'));
96+
const staleIat = Math.floor(clock.now / 1000) - 120;
97+
const { jws } = buildJws({ iat: staleIat });
98+
99+
expect(() => jwsVerify(jws, payload, publicKey, 60, [algo])).to.throw('The signature has expired');
100+
});
101+
});
102+
});

0 commit comments

Comments
 (0)