diff options
| author | Elizabeth Hunt <me@liz.coffee> | 2025-12-14 22:43:24 -0800 |
|---|---|---|
| committer | Elizabeth Hunt <me@liz.coffee> | 2025-12-14 22:43:24 -0800 |
| commit | cdb1a57614068fcfefa145bc6df45c9def7ccc6a (patch) | |
| tree | 92cadbecda8658c143b7625d5925e3411976a892 /test/token.test.ts | |
| parent | 6d318665a08c0d4564d8de23cc39425d2c0bac49 (diff) | |
| download | posthook-cdb1a57614068fcfefa145bc6df45c9def7ccc6a.tar.gz posthook-cdb1a57614068fcfefa145bc6df45c9def7ccc6a.zip | |
Updates
Diffstat (limited to 'test/token.test.ts')
| -rw-r--r-- | test/token.test.ts | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/test/token.test.ts b/test/token.test.ts new file mode 100644 index 0000000..060b591 --- /dev/null +++ b/test/token.test.ts @@ -0,0 +1,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'); + }); +}); |
