SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
1.4 KB · 36 lines typescript
Raw Blame History
1import 'server-only';2import { createHmac, timingSafeEqual } from 'node:crypto';3import { cookies } from 'next/headers';4import { redirect } from 'next/navigation';56export const ADMIN_COOKIE = 'ri_admin';78/** Cookie value = HMAC(SESSION_SECRET, ADMIN_TOKEN): the token itself never travels after login. */9export function adminCookieValue(): string | null {10  const token = process.env.ADMIN_TOKEN;11  if (!token) return null;12  return createHmac('sha256', process.env.SESSION_SECRET ?? 'dev-only').update(`admin:${token}`).digest('hex');13}1415export function isValidAdminCookie(value: string | undefined | null): boolean {16  const expected = adminCookieValue();17  if (!expected || !value || value.length !== expected.length) return false;18  return timingSafeEqual(Buffer.from(value), Buffer.from(expected));19}2021export function tokenMatches(token: string): boolean {22  const expected = process.env.ADMIN_TOKEN;23  if (!expected || token.length !== expected.length) return false;24  return timingSafeEqual(Buffer.from(token), Buffer.from(expected));25}2627export async function isAdmin(): Promise<boolean> {28  const jar = await cookies();29  return isValidAdminCookie(jar.get(ADMIN_COOKIE)?.value);30}3132/** Server-component guard (the proxy already blocks unauthenticated requests; this is defence in depth). */33export async function requireAdmin(): Promise<void> {34  if (!(await isAdmin())) redirect('/admin/login');35}36