import { describe, expect, it } from 'vitest'; import crypto from 'node:crypto'; import { hashPasswordArgon2id, randomToken, sha256Token, signAccessToken, verifyAccessToken, verifyPasswordArgon2id } from './index.js'; describe('auth primitives', () => { it('hashes and verifies Argon2id passwords with PHC metadata', async () => { const secret = crypto.randomUUID(); const hash = await hashPasswordArgon2id(secret, { salt: Buffer.alloc(16, 7), memoryKiB: 1024, passes: 1 }); expect(hash).toMatch(/^\$argon2id\$v=19\$m=1024,t=1,p=1\$/); await expect(verifyPasswordArgon2id(secret, hash)).resolves.toBe(true); await expect(verifyPasswordArgon2id(crypto.randomUUID(), hash)).resolves.toBe(false); }); it('hashes refresh tokens without retaining token material', () => { const token = randomToken(); const digest = sha256Token(token); expect(token).not.toContain(digest); expect(digest).toMatch(/^[a-f0-9]{64}$/); }); it('signs and verifies expiring access tokens', () => { const secret = 'test-only-access-token-secret-min-32-bytes'; const token = signAccessToken( { sub: 'usr_test', username: 'operator', roles: ['admin'], typ: 'access' }, { secret, issuer: 'lisglosips-api', audience: 'lisglosips-web', ttlSeconds: 60, now: new Date('2026-06-21T00:00:00.000Z') } ); expect( verifyAccessToken(token, { secret, issuer: 'lisglosips-api', audience: 'lisglosips-web', now: new Date('2026-06-21T00:00:30.000Z') })?.sub ).toBe('usr_test'); expect( verifyAccessToken(token, { secret, issuer: 'lisglosips-api', audience: 'lisglosips-web', now: new Date('2026-06-21T00:02:00.000Z') }) ).toBeNull(); }); });