import { describe, expect, test } from 'bun:test'; import { randomBytes } from 'node:crypto'; import { generateKeyPair, exportPKCS8, jwtVerify } from 'jose'; import { TokenCipher, randomSecret, hashSecret, pkceChallenge, applicationJwt, assertSameOrigin, assertOAuthSessionBinding } from '../src/lib/server/security'; describe('server credential boundaries', () => { test('authenticated encryption binds purpose and detects tampering', async () => { const cipher = new TokenCipher(randomBytes(32).toString('base64')); const envelope = await cipher.seal({ refresh: 'never-in-browser' }, 'character:123'); expect(envelope).not.toContain('never-in-browser'); expect(await cipher.open<{ refresh: string }>(envelope, 'character:123')).toEqual({ refresh: 'never-in-browser' }); await expect(cipher.open(envelope, 'character:456')).rejects.toThrow(); const parts = envelope.split('.'); parts[2] = (parts[2]![0] === 'A' ? 'B' : 'A') + parts[2]!.slice(1); await expect(cipher.open(parts.join('.'), 'character:123')).rejects.toThrow(); expect(() => new TokenCipher('short')).toThrow(); }); test('opaque secrets and PKCE S256 vector', () => { expect(randomSecret()).not.toBe(randomSecret()); expect(hashSecret(randomSecret())).toHaveLength(64); expect(pkceChallenge('dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk')).toBe('E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM'); }); test('application token audience is distinct from EVE and is short-lived', async () => { const { privateKey, publicKey } = await generateKeyPair('RS256', { extractable: true }); const token = await applicationJwt({ userId: 'u1', sessionId: 's1', issuer: 'https://app.example', privateKey: Buffer.from(await exportPKCS8(privateKey)).toString('base64') }); const { payload } = await jwtVerify(token, publicKey, { issuer: 'https://app.example', audience: 'eve-intelligence' }); expect(payload.sub).toBe('u1'); expect(payload.sid).toBe('s1'); expect(payload.exp! - payload.iat!).toBe(300); await expect(jwtVerify(token, publicKey, { audience: 'EVE Online' })).rejects.toThrow(); }); test('OAuth linking cannot switch user or browser session during the round-trip', () => { const cookie = randomSecret(); const binding = { expectedUserId: 'user1', expectedSessionHash: hashSecret(cookie), currentUserId: 'user1', currentCookie: cookie }; assertOAuthSessionBinding(binding); expect(() => assertOAuthSessionBinding({ ...binding, currentUserId: 'user2' })).toThrow(); expect(() => assertOAuthSessionBinding({ ...binding, currentCookie: randomSecret() })).toThrow(); expect(() => assertOAuthSessionBinding({ ...binding, currentUserId: null })).toThrow(); expect(() => assertOAuthSessionBinding({ expectedSessionHash: null, currentUserId: 'unexpected-login' })).toThrow(); assertOAuthSessionBinding({ expectedSessionHash: null, currentUserId: null }); }); test('state-changing browser actions reject missing/foreign origins', () => { expect(() => assertSameOrigin(new Request('https://app.example/api', { method: 'POST' }), 'https://app.example')).toThrow(); expect(() => assertSameOrigin(new Request('https://app.example/api', { headers: { origin: 'https://evil.example' } }), 'https://app.example')).toThrow(); assertSameOrigin(new Request('https://app.example/api', { headers: { origin: 'https://app.example' } }), 'https://app.example'); }); });