import { getSessionForSSR } from "supertokens-node/custom"; import { ensureSuperTokensBackend } from "../server"; import { logger } from "../"; import SuperTokens from "supertokens-node"; const USER_PHONE_TTL_MS = 60 * 1000; const userPhoneCache = new Map(); async function getUserPhone(userId: string): Promise { const now = Date.now(); const cached = userPhoneCache.get(userId); if (cached && cached.expiresAt > now) { return cached.phone; } const user = await SuperTokens.getUser(userId); const phone = user?.phoneNumbers[0]; userPhoneCache.set(userId, { phone, expiresAt: now + USER_PHONE_TTL_MS }); return phone; } export async function getSessionForStart(request: Request, options?: { sessionRequired?: boolean }) { ensureSuperTokensBackend(); const cookieHeader = request.headers.get('cookie'); if (cookieHeader) { const tokens = cookieHeader.match(/sAccessToken=([^;]+)/g); if (tokens && tokens.length > 1) { logger.warn(`Detected ${tokens.length} duplicate sAccessToken cookies, cleaning up`); const parsedTokens = tokens.map(tokenStr => { const token = tokenStr.replace('sAccessToken=', ''); try { const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()); return { token, exp: payload.exp, iat: payload.iat }; } catch (e) { logger.error('Failed to parse token', e); return { token, exp: 0, iat: 0 }; } }); parsedTokens.sort((a, b) => b.exp - a.exp); const freshestToken = parsedTokens[0]; logger.info(`Using freshest token: exp=${freshestToken.exp}, iat=${freshestToken.iat}`); const cleanedCookie = cookieHeader .split(';') .filter(c => !c.trim().startsWith('sAccessToken=')) .join(';') + `; sAccessToken=${freshestToken.token}`; const cleanedHeaders = new Headers(request.headers); cleanedHeaders.set('cookie', cleanedCookie); request = new Request(request, { headers: cleanedHeaders }); } } try { const session = await getSessionForSSR(request); if (session.hasToken) { const userId = session.accessTokenPayload?.sub; if (userId === undefined || session.accessTokenPayload?.sessionHandle === undefined) { return { hasToken: true, needsRefresh: true, error: 'TRY_REFRESH_TOKEN' } } const phone = await getUserPhone(userId); return { hasToken: true, accessTokenPayload: session.accessTokenPayload, userId, sessionHandle: session.accessTokenPayload?.sessionHandle, phone }; } return null; } catch (error: any) { logger.error("Session error", error); if (error.type === "TRY_REFRESH_TOKEN") { return { hasToken: false, needsRefresh: true, error: 'TRY_REFRESH_TOKEN' }; } if (options?.sessionRequired === false) { return null; } throw error; } }