Files
flxn-app/src/utils/supertokens.ts
T

155 lines
4.1 KiB
TypeScript

import {
createMiddleware,
createServerFn,
createServerOnlyFn,
} from "@tanstack/react-start";
import { getRequest } from "@tanstack/react-start/server";
import { Logger } from "@/lib/logger";
import { isAdminRole } from "@/features/core/utils/roles";
import z from "zod";
const logger = new Logger("Middleware");
export const PUBLIC_ROUTES = ["/login", "/logout", "/refresh-session"];
export const getSessionContext = createServerOnlyFn(
async (request: Request, options?: { isServerFunction?: boolean }) => {
const { getSessionContextCore } = await import("./supertokens-core.server");
return getSessionContextCore(request, options);
}
);
export const superTokensRequestMiddleware = createMiddleware({
type: "request",
}).server(async ({ next, request }) => {
const context = await getSessionContext(request);
return next({ context });
});
export const superTokensOptionalRequestMiddleware = createMiddleware({
type: "request",
}).server(async ({ next, request }) => {
type SessionContext = Awaited<ReturnType<typeof getSessionContext>>;
let context: SessionContext;
try {
context = await getSessionContext(request);
} catch {
context = {
userAuthId: null,
roles: [],
metadata: undefined,
phone: undefined,
player: null,
} as unknown as SessionContext;
}
return next({ context });
});
export const superTokensFunctionMiddleware = createMiddleware({
type: "function",
}).server(async ({ next }) => {
const request = getRequest();
try {
const context = await getSessionContext(request, { isServerFunction: true });
return next({ context });
} catch (error: any) {
if (error.message === "SESSION_REFRESH_REQUIRED") {
throw new Response(
JSON.stringify({
error: "SESSION_REFRESH_REQUIRED",
message: "Session needs to be refreshed"
}),
{
status: 401,
headers: { "Content-Type": "application/json" }
}
);
}
throw error;
}
});
export const superTokensAdminFunctionMiddleware = createMiddleware({
type: "function",
}).server(async ({ next, serverFnMeta }) => {
const request = getRequest();
try {
const context = await getSessionContext(request, { isServerFunction: true });
if (isAdminRole(context.roles)) {
return next({ context });
}
logger.error("Unauthorized user in admin function.", {
userAuthId: context.userAuthId,
roles: context.roles,
});
const { recordDeniedServerFn } = await import("./activities");
recordDeniedServerFn(request, context.metadata, serverFnMeta);
throw new Error("Unauthorized");
} catch (error: any) {
if (error.message === "SESSION_REFRESH_REQUIRED") {
throw new Response(
JSON.stringify({
error: "SESSION_REFRESH_REQUIRED",
message: "Session needs to be refreshed"
}),
{
status: 401,
headers: { "Content-Type": "application/json" }
}
);
}
throw error;
}
});
export const setUserMetadata = createServerFn({ method: "POST" })
.validator(
z
.object({
first_name: z
.string()
.min(2)
.max(20)
.regex(
/^[a-zA-Z0-9\s]+$/,
"First name must be 2-20 characters long and contain only letters and spaces"
),
last_name: z
.string()
.min(2)
.max(20)
.regex(
/^[a-zA-Z0-9\s]+$/,
"Last name must be 2-20 characters long and contain only letters and spaces"
),
player_id: z.string(),
})
.partial()
)
.middleware([superTokensFunctionMiddleware])
.handler(async ({ context, data }) => {
const { userAuthId, metadata } = context;
if (!userAuthId) return;
const { updateUserMetadataFields } = await import(
"./supertokens-core.server"
);
await updateUserMetadataFields(userAuthId, {
first_name: data.first_name,
last_name: data.last_name,
player_id: data.player_id,
});
return {
metadata: {
...metadata,
...data,
},
};
});