import { createError } from 'h3'; import type { AuthInfoResponse } from '~/types/auth'; import { createUserSession, deleteUserSession, getUserSession, } from '~/server/utils/sessionStore'; type SessionStoreRequest = { accessToken?: string; refreshToken?: string; }; const DEFAULT_SESSION_SECONDS = 60 * 60; // 1 hour const parseJwtPayload = (token: string): { exp?: number } | null => { try { const payload = token.split('.')[1]; if (!payload) return null; const normalized = payload.replace(/-/g, '+').replace(/_/g, '/'); const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, '='); const decoded = Buffer.from(padded, 'base64').toString(); return JSON.parse(decoded) as { exp?: number }; } catch { return null; } }; export default defineEventHandler(async (event) => { const body = await readBody(event); const accessToken = body?.accessToken; const refreshToken = body?.refreshToken; if (!accessToken || !refreshToken) { throw createError({ statusCode: 400, statusMessage: 'accessToken and refreshToken are required', }); } const config = useRuntimeConfig(); const baseUrl = config.public.baseUrl; const authInfoUrl = `${baseUrl}/api/v1/auth/info`; let authInfoResponse: AuthInfoResponse; try { authInfoResponse = await $fetch(authInfoUrl, { method: 'GET', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}`, }, }); } catch (error: any) { throw createError({ statusCode: 401, statusMessage: error?.data?.message || error?.message || 'Failed to fetch auth info', }); } if (!authInfoResponse?.data) { throw createError({ statusCode: 502, statusMessage: 'Invalid auth info response', }); } const now = Date.now(); const refreshPayload = parseJwtPayload(refreshToken); const refreshExpSeconds = refreshPayload?.exp; const expiresAt = typeof refreshExpSeconds === 'number' && refreshExpSeconds > 0 ? refreshExpSeconds * 1000 : now + DEFAULT_SESSION_SECONDS * 1000; const maxAge = Math.max(Math.floor((expiresAt - now) / 1000), 60); const previousSessionId = getCookie(event, 'user_session'); if (previousSessionId && getUserSession(previousSessionId)) { deleteUserSession(previousSessionId); } const sessionId = createUserSession({ user: { auth_provider: authInfoResponse.data.auth_provider || 'jwt', email: authInfoResponse.data.email, name: authInfoResponse.data.name, role: authInfoResponse.data.role, user_id: authInfoResponse.data.user_id, username: authInfoResponse.data.username, }, accessToken, refreshToken, expiresAt, }); const isSecure = process.env.NODE_ENV === 'production' && event.node.req.headers['x-forwarded-proto'] === 'https'; setCookie(event, 'user_session', sessionId, { httpOnly: true, secure: isSecure, sameSite: 'lax', maxAge, path: '/', }); return { success: true, message: 'Session stored successfully', expiresAt, }; });