// shared/lib/auth/jwt.ts — 共享 JWT 签发/验证(HS256,跨站点 cookie) import crypto from 'crypto' export interface SignJwtOptions { secret: string payload: Record expiresInSeconds?: number } export function signJwt({ secret, payload, expiresInSeconds = 604800 }: SignJwtOptions): string { const header = { alg: 'HS256', typ: 'JWT' } const now = Math.floor(Date.now() / 1000) const body = { ...payload, iat: now, exp: now + expiresInSeconds } const encoded = (obj: object) => Buffer.from(JSON.stringify(obj)).toString('base64url') const signature = crypto .createHmac('sha256', secret) .update(`${encoded(header)}.${encoded(body)}`) .digest('base64url') return `${encoded(header)}.${encoded(body)}.${signature}` } export function verifyJwt(token: string, secret: string): Record | null { try { const parts = token.split('.') if (parts.length !== 3) return null const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString()) if (payload.exp && payload.exp * 1000 < Date.now()) return null const signature = crypto .createHmac('sha256', secret) .update(`${parts[0]}.${parts[1]}`) .digest('base64url') return signature === parts[2] ? payload : null } catch { return null } } // 签发共享 tlyq_session cookie 配置 export function sharedCookieConfig(domain: string, maxAge = 604800): Record { return { name: 'tlyq_session', value: '', options: { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax' as const, domain, path: '/', maxAge, }, } }