57 lines
1.5 KiB
TypeScript
57 lines
1.5 KiB
TypeScript
import { createHmac, timingSafeEqual } from 'node:crypto'
|
|
|
|
const TOKEN_COOKIE = 'admin_token'
|
|
const TOKEN_MAX_AGE = 60 * 60 * 24 // 24h
|
|
|
|
export function signToken(payload: string, secret: string): string {
|
|
const hmac = createHmac('sha256', secret)
|
|
hmac.update(payload)
|
|
return payload + '.' + hmac.digest('hex')
|
|
}
|
|
|
|
export function verifyToken(token: string, secret: string): string | null {
|
|
const dotIndex = token.lastIndexOf('.')
|
|
if (dotIndex === -1) return null
|
|
|
|
const payload = token.slice(0, dotIndex)
|
|
const expected = signToken(payload, secret)
|
|
|
|
const a = Buffer.from(token)
|
|
const b = Buffer.from(expected)
|
|
|
|
if (a.length !== b.length) return null
|
|
if (!timingSafeEqual(a, b)) return null
|
|
|
|
// Check expiry
|
|
try {
|
|
const data = JSON.parse(payload)
|
|
if (data.exp && data.exp < Math.floor(Date.now() / 1000)) return null
|
|
return payload
|
|
}
|
|
catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function setAdminCookie(event: any, secret: string): void {
|
|
const exp = Math.floor(Date.now() / 1000) + TOKEN_MAX_AGE
|
|
const payload = JSON.stringify({ role: 'admin', exp })
|
|
const token = signToken(payload, secret)
|
|
|
|
setCookie(event, TOKEN_COOKIE, token, {
|
|
httpOnly: true,
|
|
secure: process.env.NODE_ENV === 'production',
|
|
sameSite: 'lax',
|
|
maxAge: TOKEN_MAX_AGE,
|
|
path: '/',
|
|
})
|
|
}
|
|
|
|
export function clearAdminCookie(event: any): void {
|
|
deleteCookie(event, TOKEN_COOKIE, { path: '/' })
|
|
}
|
|
|
|
export function getAdminToken(event: any): string | null {
|
|
return getCookie(event, TOKEN_COOKIE) ?? null
|
|
}
|