From 4e4cc8545e324b75c6a9f023beceda0fea92ba27 Mon Sep 17 00:00:00 2001 From: Carlos Narro Date: Sat, 30 May 2026 19:34:07 +0200 Subject: [PATCH] =?UTF-8?q?Add=20generaci=C3=B3n=20y=20expiraci=C3=B3n=20d?= =?UTF-8?q?e=20tokens=20de=20sesi=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mvp/b2c/src/lib/auth/tokens.ts | 19 +++++++++++++++++ mvp/b2c/tests/auth/tokens.test.ts | 34 +++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 mvp/b2c/src/lib/auth/tokens.ts create mode 100644 mvp/b2c/tests/auth/tokens.test.ts diff --git a/mvp/b2c/src/lib/auth/tokens.ts b/mvp/b2c/src/lib/auth/tokens.ts new file mode 100644 index 0000000..8cc3266 --- /dev/null +++ b/mvp/b2c/src/lib/auth/tokens.ts @@ -0,0 +1,19 @@ +import { randomBytes, createHash } from 'node:crypto'; + +export const SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 días + +export function generateSessionToken(): string { + return randomBytes(32).toString('hex'); +} + +export function hashSessionToken(token: string): string { + return createHash('sha256').update(token).digest('hex'); +} + +export function isSessionExpired(expiresAt: Date, now: Date = new Date()): boolean { + return expiresAt.getTime() <= now.getTime(); +} + +export function sessionExpiry(now: Date = new Date()): Date { + return new Date(now.getTime() + SESSION_TTL_MS); +} diff --git a/mvp/b2c/tests/auth/tokens.test.ts b/mvp/b2c/tests/auth/tokens.test.ts new file mode 100644 index 0000000..fc6ca15 --- /dev/null +++ b/mvp/b2c/tests/auth/tokens.test.ts @@ -0,0 +1,34 @@ +import { describe, it, expect } from 'vitest'; +import { + generateSessionToken, + hashSessionToken, + isSessionExpired, + sessionExpiry, + SESSION_TTL_MS, +} from '@/lib/auth/tokens'; + +describe('tokens', () => { + it('genera tokens distintos en hex de 64 chars', () => { + const a = generateSessionToken(); + const b = generateSessionToken(); + expect(a).toMatch(/^[0-9a-f]{64}$/); + expect(a).not.toBe(b); + }); + + it('hashea el token de forma determinista y distinta al token', () => { + const t = generateSessionToken(); + expect(hashSessionToken(t)).toBe(hashSessionToken(t)); + expect(hashSessionToken(t)).not.toBe(t); + }); + + it('marca expirada una sesión pasada y válida una futura', () => { + const now = new Date('2026-05-30T12:00:00Z'); + expect(isSessionExpired(new Date('2026-05-29T12:00:00Z'), now)).toBe(true); + expect(isSessionExpired(new Date('2026-05-31T12:00:00Z'), now)).toBe(false); + }); + + it('sessionExpiry suma el TTL al instante dado', () => { + const now = new Date('2026-05-30T12:00:00Z'); + expect(sessionExpiry(now).getTime()).toBe(now.getTime() + SESSION_TTL_MS); + }); +});