import { PasswordService } from '../src/identity/password.service'; import { issueToken, hashToken } from '../src/identity/tokens'; import { createUserSchema, roleSchema } from '../src/identity/identity.schemas'; describe('passwords and token primitives', () => { const passwords = new PasswordService(); it('salts equal passwords independently and verifies them', async () => { const first = await passwords.hash('a secure long passphrase'); const second = await passwords.hash('a secure long passphrase'); expect(first).not.toBe(second); expect(await passwords.verify('a secure long passphrase', first)).toBe( true, ); expect(await passwords.verify('wrong', first)).toBe(false); await passwords.dummyVerify('anything'); }); it.each([ '', 'bcrypt$bad', 'scrypt-v1$x$x', 'scrypt-v1$' + 'a'.repeat(32) + '$' + 'a'.repeat(128) + '$extra', ])('fails closed for malformed stored hash %s', async (value) => { expect(await passwords.verify('password', value)).toBe(false); }); it('issues random URL-safe tokens and one-way digests', () => { const first = issueToken(); expect(first.token).toMatch(/^[A-Za-z0-9_-]{43}$/); expect(first.tokenHash).toMatch(/^[a-f0-9]{64}$/); expect(hashToken(first.token)).toBe(first.tokenHash); expect(issueToken().token).not.toBe(first.token); }); it('validates long passphrases, known permissions and duplicate permissions', () => { expect( createUserSchema.safeParse({ email: 'a@example.com', name: 'A', password: 'short', }).success, ).toBe(false); expect( roleSchema.safeParse({ name: 'Role', permissions: ['anything'] }).success, ).toBe(false); expect( roleSchema.safeParse({ name: 'Role', permissions: ['users.read', 'users.read'], }).success, ).toBe(false); }); });