import { redirect } from "react-router"; interface AuthLoaderData { readonly token: string | null; } export const loadAuthToken = async ( request: Request ): Promise => { const cookie = request.headers.get("cookie"); if (!cookie) { return { token: null }; } const tokenUrl = new URL( "/api/auth/convex/token", new URL(request.url).origin ); const headers = new Headers({ cookie }); const response = await fetch(tokenUrl, { headers }); if (response.status === 401) { return { token: null }; } if (!response.ok) { throw new Response("Authentication service unavailable", { status: 503 }); } const payload: unknown = await response.json(); if ( typeof payload !== "object" || payload === null || !("token" in payload) || typeof payload.token !== "string" ) { throw new Response("Authentication service returned an invalid response", { status: 502, }); } return { token: payload.token }; }; export const requireAuthToken = async ( request: Request ): Promise => { const auth = await loadAuthToken(request); if (!auth.token) { const requestUrl = new URL(request.url); const returnTo = `${requestUrl.pathname}${requestUrl.search}`; throw redirect(`/login?returnTo=${encodeURIComponent(returnTo)}`); } return auth; }; export const redirectAuthenticated = async ( request: Request ): Promise => { const auth = await loadAuthToken(request); if (auth.token) { const requestUrl = new URL(request.url); const returnTo = requestUrl.searchParams.get("returnTo"); throw redirect(returnTo?.startsWith("/") ? returnTo : "/"); } return auth; };