86 lines
2.3 KiB
JavaScript
86 lines
2.3 KiB
JavaScript
import { createHash, randomUUID } from 'node:crypto'
|
|
import { parseCookies } from './helpers.js'
|
|
|
|
const ADMIN_SESSION_COOKIE = 'vbn_admin_session'
|
|
const ADMIN_SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1000
|
|
const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD
|
|
const adminSessions = new Map()
|
|
|
|
function cookieFlags() {
|
|
return process.env.NODE_ENV === 'production' ? '; Secure' : ''
|
|
}
|
|
|
|
export function sha256(value) {
|
|
return createHash('sha256').update(String(value)).digest('hex')
|
|
}
|
|
|
|
export function isAdminPasswordConfigured() {
|
|
return Boolean(ADMIN_PASSWORD)
|
|
}
|
|
|
|
export function validateAdminPasswordSetup() {
|
|
if (!isAdminPasswordConfigured() && process.env.NODE_ENV === 'production') {
|
|
throw new Error('ADMIN_PASSWORD is required in production.')
|
|
}
|
|
|
|
if (!isAdminPasswordConfigured()) {
|
|
console.warn('ADMIN_PASSWORD is not configured; admin routes will remain disabled until the environment is configured.')
|
|
}
|
|
}
|
|
|
|
export function isAdminPasswordValid(password) {
|
|
if (!isAdminPasswordConfigured()) return false
|
|
return sha256(password) === sha256(ADMIN_PASSWORD)
|
|
}
|
|
|
|
export function createAdminSession() {
|
|
const token = randomUUID()
|
|
adminSessions.set(token, Date.now() + ADMIN_SESSION_TTL_MS)
|
|
return token
|
|
}
|
|
|
|
export function deleteAdminSession(token) {
|
|
if (token) {
|
|
adminSessions.delete(token)
|
|
}
|
|
}
|
|
|
|
export function isValidAdminSession(req) {
|
|
if (!isAdminPasswordConfigured()) return false
|
|
|
|
const cookies = parseCookies(req.headers.cookie)
|
|
const sessionToken = cookies[ADMIN_SESSION_COOKIE]
|
|
if (!sessionToken) return false
|
|
|
|
const expiresAt = adminSessions.get(sessionToken)
|
|
if (!expiresAt || expiresAt <= Date.now()) {
|
|
adminSessions.delete(sessionToken)
|
|
return false
|
|
}
|
|
|
|
adminSessions.set(sessionToken, Date.now() + ADMIN_SESSION_TTL_MS)
|
|
return true
|
|
}
|
|
|
|
export function setAdminSessionCookie(res, token) {
|
|
res.append(
|
|
'Set-Cookie',
|
|
`${ADMIN_SESSION_COOKIE}=${encodeURIComponent(token)}; Max-Age=${Math.floor(ADMIN_SESSION_TTL_MS / 1000)}; Path=/; HttpOnly; SameSite=Lax${cookieFlags()}`,
|
|
)
|
|
}
|
|
|
|
export function clearAdminSessionCookie(res) {
|
|
res.append(
|
|
'Set-Cookie',
|
|
`${ADMIN_SESSION_COOKIE}=; Max-Age=0; Path=/; HttpOnly; SameSite=Lax${cookieFlags()}`,
|
|
)
|
|
}
|
|
|
|
export function requireAdminAuth(req, res, next) {
|
|
if (!isValidAdminSession(req)) {
|
|
res.status(401).json({ message: 'Unauthorized' })
|
|
return
|
|
}
|
|
next()
|
|
}
|