import crypto from 'node:crypto'; const COOKIE_NAME = 'ppai_session'; const DEFAULT_MAX_AGE = 12; const getConfig = () => ({ username: process.env.PPAI_AUTH_USER || 'admin', password: process.env.PPAI_AUTH_PASSWORD || 'admin123', secret: process.env.PPAI_AUTH_SECRET || 'ppai-dev-secret', maxAge: Number(process.env.PPAI_AUTH_MAX_AGE || DEFAULT_MAX_AGE) * 60 * 60 * 1000, }); const base64UrlEncode = (value) => Buffer.from(value).toString('base64url'); const base64UrlDecode = (value) => Buffer.from(value, 'base64url').toString(); const sign = (payload, secret) => { return crypto.createHmac('sha256', secret).update(payload).digest('base64url'); }; const safeEqual = (a = '', b = '') => { const aBuffer = Buffer.from(a); const bBuffer = Buffer.from(b); if (aBuffer.length !== bBuffer.length) { return false; } return crypto.timingSafeEqual(aBuffer, bBuffer); }; export const cookieOptions = () => { const { maxAge } = getConfig(); return { httpOnly: true, sameSite: 'lax', secure: process.env.NODE_ENV === 'production', maxAge, path: '/', }; }; export const clearCookieOptions = () => ({ ...cookieOptions(), maxAge: 0, }); export const createSession = (username) => { const { secret, maxAge } = getConfig(); const payload = base64UrlEncode(JSON.stringify({ username, exp: Date.now() + maxAge, })); const signature = sign(payload, secret); return `${payload}.${signature}`; }; export const verifySession = (token) => { if (!token || typeof token !== 'string') { return null; } const [payload, signature] = token.split('.'); if (!payload || !signature) { return null; } const { secret } = getConfig(); const expectedSignature = sign(payload, secret); if (!safeEqual(signature, expectedSignature)) { return null; } try { const session = JSON.parse(base64UrlDecode(payload)); if (!session?.username || !session?.exp || Date.now() > session.exp) { return null; } return { username: session.username }; } catch { return null; } }; export const validateCredentials = (username, password) => { const config = getConfig(); return safeEqual(username, config.username) && safeEqual(password, config.password); }; export const authCookieName = COOKIE_NAME;