app:60 秒 HMAC 票据 —— 给 EventSource 与 <a href> 下载这两条带不了头的路(ADR-012)

This commit is contained in:
xcs
2026-08-25 17:02:08 +08:00
parent 3a34362eec
commit d66f7fad3a
2 changed files with 83 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
import { describe, expect, it } from 'vitest';
import { TICKET_TTL_MS, createTicketIssuer } from '../ticket.ts';
const issuer = createTicketIssuer(Buffer.alloc(32, 7));
const t0 = new Date('2026-08-25T09:00:00Z');
describe('60 秒票据', () => {
it('签出来能验回 sub;形状 v1.<sub>.<exp>.<mac>;到期时刻 = 签发 + 60s', () => {
const { ticket, expiresAt } = issuer.issue('sub-1', t0);
expect(ticket.split('.')).toHaveLength(4);
expect(ticket.startsWith('v1.')).toBe(true);
expect(expiresAt.getTime() - t0.getTime()).toBe(TICKET_TTL_MS);
expect(issuer.verify(ticket, new Date(t0.getTime() + 30_000))).toEqual({ staffId: 'sub-1' });
});
it('到期即拒(61 秒后)', () => {
const { ticket } = issuer.issue('sub-1', t0);
expect(issuer.verify(ticket, new Date(t0.getTime() + TICKET_TTL_MS + 1000))).toBeUndefined();
});
it('改一个字就拒:mac / sub / exp', () => {
const { ticket } = issuer.issue('sub-1', t0);
const [v, id, exp, mac] = ticket.split('.') as [string, string, string, string];
expect(issuer.verify(`${v}.${id}.${exp}.${mac.slice(0, -1)}${mac.endsWith('A') ? 'B' : 'A'}`, t0)).toBeUndefined();
expect(issuer.verify(`${v}.${Buffer.from('sub-2').toString('base64url')}.${exp}.${mac}`, t0)).toBeUndefined();
expect(issuer.verify(`${v}.${id}.${Number(exp) + 600}.${mac}`, t0)).toBeUndefined();
});
it('别的密钥签的不认', () => {
const other = createTicketIssuer(Buffer.alloc(32, 8));
expect(issuer.verify(other.issue('sub-1', t0).ticket, t0)).toBeUndefined();
});
it('垃圾输入不抛只拒', () => {
expect(issuer.verify('', t0)).toBeUndefined();
expect(issuer.verify('v2.a.b.c', t0)).toBeUndefined();
expect(issuer.verify('a.b', t0)).toBeUndefined();
expect(issuer.verify('v1...', t0)).toBeUndefined();
});
});