aboutsummaryrefslogtreecommitdiff
path: root/test/token.test.ts
blob: 060b591190b5e567ac984d9e5f0f1c888825db4d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import { TokenSigner } from '../src/token/index.js';

describe('TokenSigner', () => {
    beforeEach(() => {
        vi.useFakeTimers();
        vi.setSystemTime(new Date('2020-01-01T00:00:00.000Z'));
    });

    afterEach(() => {
        vi.useRealTimers();
    });

    it('generates and validates a token for the expected route', () => {
        const signer = new TokenSigner('test-secret', 30);

        const now = Date.now();
        const token = signer.generate('route1');

        const result = signer.validate(token, 'route1');
        expect(result.left().present()).toBe(false);
        expect(result.right().get()).toEqual({ routeName: 'route1', timestamp: now });
    });

    it('rejects token when route does not match', () => {
        const signer = new TokenSigner('test-secret', 30);
        const token = signer.generate('route1');

        const result = signer.validate(token, 'route2');
        expect(result.left().present()).toBe(true);
        expect(result.left().get().message).toBe('Token route mismatch');
    });

    it('rejects expired tokens', () => {
        const signer = new TokenSigner('test-secret', 1);
        const token = signer.generate('route1');

        vi.advanceTimersByTime(2000);

        const result = signer.validate(token, 'route1');
        expect(result.left().present()).toBe(true);
        expect(result.left().get().message).toBe('Token expired');
    });

    it('rejects tokens with a bad signature', () => {
        const signer = new TokenSigner('test-secret', 30);
        const token = signer.generate('route1');

        const decoded = Buffer.from(token, 'base64url').toString('utf-8');
        const dotIndex = decoded.lastIndexOf('.');
        const payload = decoded.substring(0, dotIndex);
        const signature = decoded.substring(dotIndex + 1);

        const parsed = JSON.parse(payload) as { routeName: string; timestamp: number };
        const tamperedPayload = JSON.stringify({ ...parsed, routeName: 'route2' });
        const tamperedToken = Buffer.from(`${tamperedPayload}.${signature}`).toString('base64url');

        const result = signer.validate(tamperedToken, 'route2');
        expect(result.left().present()).toBe(true);
        expect(result.left().get().message).toBe('Invalid token signature');
    });

    it('rejects invalid token format', () => {
        const signer = new TokenSigner('test-secret', 30);
        const invalidToken = Buffer.from('missing-dot').toString('base64url');

        const result = signer.validate(invalidToken, 'route1');
        expect(result.left().present()).toBe(true);
        expect(result.left().get().message).toBe('Invalid token format');
    });
});