diff --git a/packages/toolpad-app/src/runtime/ToolpadApp.tsx b/packages/toolpad-app/src/runtime/ToolpadApp.tsx index 021ede3fa03..ebe58b62314 100644 --- a/packages/toolpad-app/src/runtime/ToolpadApp.tsx +++ b/packages/toolpad-app/src/runtime/ToolpadApp.tsx @@ -1626,7 +1626,7 @@ export default function ToolpadApp({ rootRef, basename, state }: ToolpadAppProps (window as any).toggleDevtools = () => toggleDevtools(); }, [toggleDevtools]); - const authContext = useAuth({ dom, basename }); + const authContext = useAuth({ dom, basename, signInPagePath: `${basename}/signin` }); const appHost = useNonNullableContext(AppHostContext); const showPreviewHeader: boolean = !!appHost.isPreview && !appHost.isCanvas; diff --git a/packages/toolpad-app/src/runtime/useAuth.ts b/packages/toolpad-app/src/runtime/useAuth.ts index c7d1aad6efc..dc7dcb87abc 100644 --- a/packages/toolpad-app/src/runtime/useAuth.ts +++ b/packages/toolpad-app/src/runtime/useAuth.ts @@ -12,6 +12,10 @@ const AUTH_SIGNOUT_PATH = `${AUTH_API_PATH}/signout`; export type AuthProvider = 'github' | 'google' | 'azure-ad' | 'credentials'; +function isResponseJSON(response: Response): boolean { + return response.headers.get('content-type')?.includes('application/json') || false; +} + export interface AuthSession { user: { name: string; @@ -48,9 +52,10 @@ export const AuthContext = React.createContext({ interface UseAuthInput { dom: appDom.RenderTree; basename: string; + signInPagePath?: string; } -export function useAuth({ dom, basename }: UseAuthInput): AuthPayload { +export function useAuth({ dom, basename, signInPagePath }: UseAuthInput): AuthPayload { const authProviders = React.useMemo(() => { const app = appDom.getApp(dom); const authProviderConfigs = app.attributes.authentication?.providers ?? []; @@ -71,7 +76,9 @@ export function useAuth({ dom, basename }: UseAuthInput): AuthPayload { 'Content-Type': 'application/json', }, }); - csrfToken = (await csrfResponse.json())?.csrfToken; + if (isResponseJSON(csrfResponse)) { + csrfToken = (await csrfResponse.json())?.csrfToken; + } } catch (error) { console.error((error as Error).message); } @@ -100,8 +107,10 @@ export function useAuth({ dom, basename }: UseAuthInput): AuthPayload { setSession(null); setIsSigningOut(false); - window.location.replace(`${basename}${AUTH_SIGNIN_PATH}`); - }, [basename, getCsrfToken]); + if (!signInPagePath || window.location.pathname !== signInPagePath) { + window.location.href = `${basename}${AUTH_SIGNIN_PATH}`; + } + }, [basename, getCsrfToken, signInPagePath]); const getSession = React.useCallback(async () => { setIsSigningIn(true); @@ -115,7 +124,11 @@ export function useAuth({ dom, basename }: UseAuthInput): AuthPayload { 'Content-Type': 'application/json', }, }); - setSession(await sessionResponse.json()); + if (isResponseJSON(sessionResponse)) { + setSession(await sessionResponse.json()); + } else { + signOut(); + } } catch (error) { console.error((error as Error).message); signOut(); diff --git a/packages/toolpad-app/src/server/auth.ts b/packages/toolpad-app/src/server/auth.ts index c3c9932a85b..19a8f8f525d 100644 --- a/packages/toolpad-app/src/server/auth.ts +++ b/packages/toolpad-app/src/server/auth.ts @@ -146,13 +146,6 @@ export function createAuthHandler(project: ToolpadProject): Router { const googleProvider = GoogleProvider({ clientId: process.env.TOOLPAD_GOOGLE_CLIENT_ID, clientSecret: process.env.TOOLPAD_GOOGLE_CLIENT_SECRET, - authorization: { - params: { - prompt: 'consent', - access_type: 'offline', - response_type: 'code', - }, - }, }); const azureADProvider = AzureADProvider({ diff --git a/packages/toolpad-app/src/server/index.ts b/packages/toolpad-app/src/server/index.ts index c4e0a6b3d33..1f0456f194d 100644 --- a/packages/toolpad-app/src/server/index.ts +++ b/packages/toolpad-app/src/server/index.ts @@ -29,6 +29,9 @@ import { createRpcServer as createProjectRpcServer } from './projectRpcServer'; import { createRpcServer as createRuntimeRpcServer } from './runtimeRpcServer'; import { createAuthHandler, createRequireAuthMiddleware, getRequireAuthentication } from './auth'; +// crypto must be polyfilled to use @auth/core in Node 18 or lower +globalThis.crypto ??= (await import('node:crypto')) as Crypto; + const currentDirectory = url.fileURLToPath(new URL('.', import.meta.url)); const DEFAULT_PORT = 3000;