diff --git a/www/app/lib/apiClient.tsx b/www/app/lib/apiClient.tsx index 86f8f161..a5cec06b 100644 --- a/www/app/lib/apiClient.tsx +++ b/www/app/lib/apiClient.tsx @@ -3,8 +3,10 @@ import createClient from "openapi-fetch"; import type { paths } from "../reflector-api"; import createFetchClient from "openapi-react-query"; -import { assertExistsAndNonEmptyString } from "./utils"; +import { assertExistsAndNonEmptyString, parseNonEmptyString } from "./utils"; import { isBuildPhase } from "./next"; +import { getSession } from "next-auth/react"; +import { assertExtendedToken } from "./types"; export const API_URL = !isBuildPhase ? assertExistsAndNonEmptyString( @@ -21,29 +23,29 @@ export const client = createClient({ baseUrl: API_URL, }); -const waitForAuthTokenDefinitivePresenceOrAbscence = async () => { - let tries = 0; - let time = 0; - const STEP = 100; - while (currentAuthToken === undefined) { - await new Promise((resolve) => setTimeout(resolve, STEP)); - time += STEP; - tries++; - // most likely first try is more than enough, if it's more there's already something weird happens - if (tries > 10) { - // even when there's no auth assumed at all, we probably should explicitly call configureApiAuth(null) - throw new Error( - `Could not get auth token definitive presence/absence in ${time}ms. not calling configureApiAuth?`, - ); - } +// will assert presence/absence of login initially +const initialSessionPromise = getSession(); + +const waitForAuthTokenDefinitivePresenceOrAbsence = async () => { + const initialSession = await initialSessionPromise; + if (currentAuthToken === undefined) { + currentAuthToken = + initialSession === null + ? null + : assertExtendedToken(initialSession).accessToken; } + // otherwise already overwritten by external forces + return currentAuthToken; }; client.use({ async onRequest({ request }) { - await waitForAuthTokenDefinitivePresenceOrAbscence(); - if (currentAuthToken) { - request.headers.set("Authorization", `Bearer ${currentAuthToken}`); + const token = await waitForAuthTokenDefinitivePresenceOrAbsence(); + if (token !== null) { + request.headers.set( + "Authorization", + `Bearer ${parseNonEmptyString(token)}`, + ); } // XXX Only set Content-Type if not already set (FormData will set its own boundary) // This is a work around for uploading file, we're passing a formdata diff --git a/www/app/lib/types.ts b/www/app/lib/types.ts index af5625ec..7bcb522b 100644 --- a/www/app/lib/types.ts +++ b/www/app/lib/types.ts @@ -21,7 +21,7 @@ export interface CustomSession extends Session { // assumption that JWT is JWTWithAccessToken - we set it in jwt callback of auth; typing isn't strong around there // but the assumption is crucial to auth working export const assertExtendedToken = ( - t: T, + t: Exclude, ): T & { accessTokenExpires: number; accessToken: string; @@ -45,7 +45,7 @@ export const assertExtendedToken = ( }; export const assertExtendedTokenAndUserId = ( - t: T, + t: Exclude, ): T & { accessTokenExpires: number; accessToken: string; @@ -55,7 +55,7 @@ export const assertExtendedTokenAndUserId = ( } => { const extendedToken = assertExtendedToken(t); if (typeof (extendedToken.user as any)?.id === "string") { - return t as T & { + return t as Exclude & { accessTokenExpires: number; accessToken: string; user: U & { @@ -67,7 +67,9 @@ export const assertExtendedTokenAndUserId = ( }; // best attempt to check the session is valid -export const assertCustomSession = (s: S): CustomSession => { +export const assertCustomSession = ( + s: Exclude, +): CustomSession => { const r = assertExtendedTokenAndUserId(s); // no other checks for now return r as CustomSession;