76 lines
2.8 KiB
TypeScript
76 lines
2.8 KiB
TypeScript
import { afterEach, describe, expect, test, vi } from 'vitest';
|
|
|
|
import { createAccessToken, verifyAccessToken } from '@/lib/server/access-token';
|
|
import { verifyAccessTokenWithWebCrypto } from '@/lib/server/access-token-edge';
|
|
import {
|
|
DEFAULT_ACCESS_CODE_SESSION_TTL_SECONDS,
|
|
resolveAccessCodeSessionTtlSeconds,
|
|
} from '@/lib/server/access-token-policy';
|
|
|
|
const NOW_MS = Date.parse('2026-06-25T00:00:00Z');
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllEnvs();
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
describe('access token signing', () => {
|
|
test('verifies tokens signed with the same access code', () => {
|
|
const token = createAccessToken('demo-code', NOW_MS);
|
|
|
|
expect(verifyAccessToken(token, 'demo-code', { nowMs: NOW_MS + 1 })).toBe(true);
|
|
expect(verifyAccessToken(token, 'other-code', { nowMs: NOW_MS + 1 })).toBe(false);
|
|
expect(verifyAccessToken('bad-token', 'demo-code', { nowMs: NOW_MS + 1 })).toBe(false);
|
|
});
|
|
|
|
test('rejects expired and future-issued tokens at both Node and Edge boundaries', async () => {
|
|
const token = createAccessToken('demo-code', NOW_MS);
|
|
const ttlSeconds = 60;
|
|
|
|
expect(verifyAccessToken(token, 'demo-code', { nowMs: NOW_MS + 59_999, ttlSeconds })).toBe(
|
|
true,
|
|
);
|
|
expect(verifyAccessToken(token, 'demo-code', { nowMs: NOW_MS + 60_000, ttlSeconds })).toBe(
|
|
false,
|
|
);
|
|
expect(verifyAccessToken(token, 'demo-code', { nowMs: NOW_MS - 1, ttlSeconds })).toBe(false);
|
|
|
|
await expect(
|
|
verifyAccessTokenWithWebCrypto(token, 'demo-code', {
|
|
nowMs: NOW_MS + 60_000,
|
|
ttlSeconds,
|
|
}),
|
|
).resolves.toBe(false);
|
|
await expect(
|
|
verifyAccessTokenWithWebCrypto(token, 'demo-code', {
|
|
nowMs: NOW_MS - 1,
|
|
ttlSeconds,
|
|
}),
|
|
).resolves.toBe(false);
|
|
});
|
|
|
|
test('uses the same signature semantics in Node and Edge and rejects tampering', async () => {
|
|
const token = createAccessToken('demo-code', NOW_MS);
|
|
const tampered = `${token.slice(0, -1)}${token.endsWith('0') ? '1' : '0'}`;
|
|
|
|
await expect(
|
|
verifyAccessTokenWithWebCrypto(token, 'demo-code', { nowMs: NOW_MS + 1 }),
|
|
).resolves.toBe(true);
|
|
await expect(
|
|
verifyAccessTokenWithWebCrypto(tampered, 'demo-code', { nowMs: NOW_MS + 1 }),
|
|
).resolves.toBe(false);
|
|
expect(verifyAccessToken(tampered, 'demo-code', { nowMs: NOW_MS + 1 })).toBe(false);
|
|
});
|
|
|
|
test('resolves a configurable bounded TTL with a seven-day default', () => {
|
|
expect(resolveAccessCodeSessionTtlSeconds(undefined)).toBe(
|
|
DEFAULT_ACCESS_CODE_SESSION_TTL_SECONDS,
|
|
);
|
|
expect(resolveAccessCodeSessionTtlSeconds('3600')).toBe(3600);
|
|
expect(resolveAccessCodeSessionTtlSeconds('0')).toBe(DEFAULT_ACCESS_CODE_SESSION_TTL_SECONDS);
|
|
expect(resolveAccessCodeSessionTtlSeconds('31536001')).toBe(
|
|
DEFAULT_ACCESS_CODE_SESSION_TTL_SECONDS,
|
|
);
|
|
});
|
|
});
|