monacousa-portal/server/utils/session.ts

86 lines
2.5 KiB
TypeScript

import { serialize, parse } from 'cookie';
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
import type { SessionData } from '~/utils/types';
export class SessionManager {
private encryptionKey: Buffer;
private cookieName = 'monacousa-session';
constructor(encryptionKey: string) {
this.encryptionKey = Buffer.from(encryptionKey, 'hex');
}
private encrypt(data: string): string {
const iv = randomBytes(16);
const cipher = createCipheriv('aes-256-cbc', this.encryptionKey, iv);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
return iv.toString('hex') + ':' + encrypted;
}
private decrypt(encryptedData: string): string {
const [ivHex, encrypted] = encryptedData.split(':');
const iv = Buffer.from(ivHex, 'hex');
const decipher = createDecipheriv('aes-256-cbc', this.encryptionKey, iv);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
createSession(sessionData: SessionData): string {
const data = JSON.stringify(sessionData);
const encrypted = this.encrypt(data);
const cookieDomain = process.env.COOKIE_DOMAIN || undefined;
console.log('🍪 Creating session cookie with domain:', cookieDomain);
return serialize(this.cookieName, encrypted, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
domain: cookieDomain,
maxAge: 60 * 60 * 24 * 7, // 7 days
path: '/',
});
}
getSession(cookieHeader?: string): SessionData | null {
if (!cookieHeader) return null;
const cookies = parse(cookieHeader);
const sessionCookie = cookies[this.cookieName];
if (!sessionCookie) return null;
try {
const decrypted = this.decrypt(sessionCookie);
const sessionData = JSON.parse(decrypted) as SessionData;
// Check if session is expired
if (Date.now() > sessionData.tokens.expiresAt) {
return null;
}
return sessionData;
} catch (error) {
console.error('Failed to decrypt session:', error);
return null;
}
}
destroySession(): string {
return serialize(this.cookieName, '', {
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 0,
path: '/',
});
}
}
export function createSessionManager(): SessionManager {
const config = useRuntimeConfig();
return new SessionManager(config.encryptionKey);
}