Auth in layout?

Is it safe to do auth in layout components? For instance if I have a function like so and call it in the layout component with createAsync will it work on all page changes within that layout? Or is it like Nextjs where it should be called per page?
export const authOrRedirect = cache(async (url?: string) => {
"use server";

const sess = await getAuthSession();

if (!sess.data.user) {
return redirect(url || "/auth/login", 302);
}

const dbSess = await getUnexpiredSession(sess.data.id);

if (!dbSess) {
await logout();
return redirect(url || "/auth/login", 302);
}

return sess;
}, "authRedirect");
export const authOrRedirect = cache(async (url?: string) => {
"use server";

const sess = await getAuthSession();

if (!sess.data.user) {
return redirect(url || "/auth/login", 302);
}

const dbSess = await getUnexpiredSession(sess.data.id);

if (!dbSess) {
await logout();
return redirect(url || "/auth/login", 302);
}

return sess;
}, "authRedirect");
7 Replies
brenelz
brenelz4w ago
I think doing in layout components is fine
Mapo Doofus
Mapo Doofus4w ago
I tried it by adding it to (main).tsx as the root layout and navigating from / -> /feed. I added a console.debug to the function and it only logs one time. So it seems like if I lost or expired session between navigating it wouldn't catch it.
No description
Mapo Doofus
Mapo Doofus4w ago
export default function MainLayout(props: { children: JSX.Element }) {
createAsync(() => authOrRedirect(), { name: "authProtect" });

return (
<div class="h-full flex flex-col main-layout">
<Title>Laserfeed</Title>
<Header />
<main class="flex-grow max-w-screen-lg mx-auto w-full">
{props.children}
</main>
</div>
);
}
export default function MainLayout(props: { children: JSX.Element }) {
createAsync(() => authOrRedirect(), { name: "authProtect" });

return (
<div class="h-full flex flex-col main-layout">
<Title>Laserfeed</Title>
<Header />
<main class="flex-grow max-w-screen-lg mx-auto w-full">
{props.children}
</main>
</div>
);
}
Eve
Eve4w ago
instead of trying to force a redirect, I use this in App()
<Show when={user()} fallback={<Login />}>
<Suspense>{props.children}</Suspense>
</Show>
<Show when={user()} fallback={<Login />}>
<Suspense>{props.children}</Suspense>
</Show>
The instant user() becomes empty, the app displays the Login component. User is in a context, and every component calls setUser(checkAuth(user())) at the top.
Mapo Doofus
Mapo Doofus4w ago
Thanks, that's an interesting approach. Can you reflect in the url and history that they've moved to a different page like /auth/login if you wanted to?
Eve
Eve4w ago
I haven't tried, I mostly stick to SPAs. presumably this would work. https://developer.mozilla.org/en-US/docs/Web/API/History_API/Working_with_the_History_API
Adam Goldstein
This is Solid Start right? I ended up going with a <ProtectRoute> component which has an SSR-blocking resource for auth, and conditionally renders a <Navigate> component once auth is resolved as unauthenticated. Seems to work great with layouts
Want results from more Discord servers?
Add your server