From 86c1389d675eead5f17550cb7aad8d727a8a2e95 Mon Sep 17 00:00:00 2001 From: Justin Reiners Date: Thu, 26 Mar 2026 11:54:35 -0500 Subject: [PATCH] feat: add auth utilities (Argon2, session tokens) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/lib/server/auth-utils.ts | 22 ++++++++++++++++++++++ tests/unit/auth.test.ts | 29 +++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/lib/server/auth-utils.ts create mode 100644 tests/unit/auth.test.ts diff --git a/src/lib/server/auth-utils.ts b/src/lib/server/auth-utils.ts new file mode 100644 index 0000000..53123f0 --- /dev/null +++ b/src/lib/server/auth-utils.ts @@ -0,0 +1,22 @@ +import { hash, verify } from '@node-rs/argon2'; +import crypto from 'crypto'; + +export async function hashPassword(password: string): Promise { + return hash(password); +} + +export async function verifyPassword(hashedPassword: string, password: string): Promise { + try { + return await verify(hashedPassword, password); + } catch { + return false; + } +} + +export function generateSessionToken(): string { + return crypto.randomBytes(32).toString('hex'); +} + +export function hashSessionToken(token: string): string { + return crypto.createHash('sha256').update(token).digest('hex'); +} diff --git a/tests/unit/auth.test.ts b/tests/unit/auth.test.ts new file mode 100644 index 0000000..6f1bac0 --- /dev/null +++ b/tests/unit/auth.test.ts @@ -0,0 +1,29 @@ +import { describe, it, expect } from 'vitest'; +import { + hashPassword, verifyPassword, generateSessionToken, hashSessionToken +} from '$lib/server/auth-utils'; + +describe('auth-utils', () => { + it('hashes and verifies a password', async () => { + const hash = await hashPassword('testpassword'); + expect(hash).not.toBe('testpassword'); + expect(await verifyPassword(hash, 'testpassword')).toBe(true); + expect(await verifyPassword(hash, 'wrongpassword')).toBe(false); + }); + + it('generates unique session tokens', () => { + const token1 = generateSessionToken(); + const token2 = generateSessionToken(); + expect(token1).toHaveLength(64); + expect(token2).toHaveLength(64); + expect(token1).not.toBe(token2); + }); + + it('hashes session tokens deterministically', () => { + const token = generateSessionToken(); + const hash1 = hashSessionToken(token); + const hash2 = hashSessionToken(token); + expect(hash1).toBe(hash2); + expect(hash1).not.toBe(token); + }); +});