// types.ts // // Authentication types and Zod schemas. import { z } from "zod"; // Branded type for token IDs (the hash, not the raw token) export type TokenId = string & { readonly __brand: "TokenId" }; // Token types for different purposes export const tokenTypeParser = z.enum([ "session", "password_reset", "email_verify", ]); export type TokenType = z.infer; // Authentication method - how the token was delivered export const authMethodParser = z.enum(["cookie", "bearer"]); export type AuthMethod = z.infer; // Session data schema - what gets stored export const sessionDataParser = z.object({ tokenId: z.string().min(1), userId: z.string().min(1), tokenType: tokenTypeParser, authMethod: authMethodParser, createdAt: z.coerce.date(), expiresAt: z.coerce.date(), lastUsedAt: z.coerce.date().optional(), userAgent: z.string().optional(), ipAddress: z.string().optional(), isUsed: z.boolean().optional(), // For one-time tokens }); export type SessionData = z.infer; // Input validation schemas for auth endpoints export const loginInputParser = z.object({ email: z.string().email(), password: z.string().min(1), }); export const registerInputParser = z.object({ email: z.string().email(), password: z.string().min(8), displayName: z.string().optional(), }); export const forgotPasswordInputParser = z.object({ email: z.string().email(), }); export const resetPasswordInputParser = z.object({ token: z.string().min(1), password: z.string().min(8), }); // Token lifetimes in milliseconds export const tokenLifetimes: Record = { session: 30 * 24 * 60 * 60 * 1000, // 30 days password_reset: 1 * 60 * 60 * 1000, // 1 hour email_verify: 24 * 60 * 60 * 1000, // 24 hours }; // Import here to avoid circular dependency at module load time import { AnonymousUser, type MaybeUser } from "../user"; // Session wrapper class providing a consistent interface for handlers. // Always present on Call (never null), but may represent an anonymous session. export class Session { constructor( private readonly data: SessionData | null, private readonly user: MaybeUser, ) {} getUser(): MaybeUser { return this.user; } getData(): SessionData | null { return this.data; } isAuthenticated(): boolean { return this.user !== AnonymousUser; } get tokenId(): string | undefined { return this.data?.tokenId; } get userId(): string | undefined { return this.data?.userId; } }