Files
flxn-app/src/lib/supertokens/recipes/start-session.ts
T
2026-07-12 21:26:16 -07:00

101 lines
3.0 KiB
TypeScript

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<string, { phone: string | undefined; expiresAt: number }>();
async function getUserPhone(userId: string): Promise<string | undefined> {
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;
}
}