| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- import crypto from 'node:crypto';
- const COOKIE_NAME = 'ppai_session';
- const DEFAULT_MAX_AGE = 12;
- const DEFAULT_REFRESH_THRESHOLD = 2;
- const getConfig = () => {
- return {
- 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,
- refreshThreshold: Number(process.env.PPAI_AUTH_REFRESH_THRESHOLD || DEFAULT_REFRESH_THRESHOLD) * 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 refreshSessionIfNeeded = (res, session) => {
- if (!session?.username || !session?.exp) {
- return false;
- }
- const { refreshThreshold } = getConfig();
- const remainingTime = session.exp - Date.now();
- if (remainingTime > refreshThreshold) {
- return false;
- }
- res.cookie(authCookieName, createSession(session.username), cookieOptions());
- return true;
- };
- 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, exp: session.exp };
- }
- 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;
|