TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import 'server-only';2import { cookies } from 'next/headers';3import { and, eq, gt, isNull } from '@/lib/db';4import { newId } from '@rareindex/shared';5import { db, trustedDevices } from '@/lib/db';6import { hmacToken, randomToken } from './crypto';7import { clientInfo, deviceLabel } from './request';8import { DEVICE_COOKIE } from './session';910const TRUST_DAYS = 30;1112/** Is the current browser a trusted device for this user? */13export async function isTrustedDevice(userId: string): Promise<boolean> {14 const raw = (await cookies()).get(DEVICE_COOKIE)?.value;15 if (!raw) return false;16 const rows = await db()17 .select({ id: trustedDevices.id })18 .from(trustedDevices)19 .where(and(eq(trustedDevices.userId, userId), eq(trustedDevices.tokenHash, hmacToken(raw, 'device')), isNull(trustedDevices.revokedAt), gt(trustedDevices.expiresAt, new Date())))20 .limit(1);21 if (!rows[0]) return false;22 void db().update(trustedDevices).set({ lastUsedAt: new Date() }).where(eq(trustedDevices.id, rows[0].id)).catch(() => {});23 return true;24}2526export async function trustThisDevice(userId: string): Promise<void> {27 const raw = randomToken(32);28 const { ip, userAgent } = await clientInfo();29 const expiresAt = new Date(Date.now() + TRUST_DAYS * 86_400_000);30 await db().insert(trustedDevices).values({ id: newId('event'), userId, tokenHash: hmacToken(raw, 'device'), label: deviceLabel(userAgent), userAgent, ip, expiresAt, lastUsedAt: new Date() });31 (await cookies()).set(DEVICE_COOKIE, raw, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', path: '/', expires: expiresAt });32}3334export async function revokeDevice(userId: string, deviceId: string): Promise<void> {35 await db().update(trustedDevices).set({ revokedAt: new Date() }).where(and(eq(trustedDevices.id, deviceId), eq(trustedDevices.userId, userId)));36}3738export async function revokeAllDevices(userId: string): Promise<void> {39 await db().update(trustedDevices).set({ revokedAt: new Date() }).where(and(eq(trustedDevices.userId, userId), isNull(trustedDevices.revokedAt)));40}41