From a1a7ba52417cbe4af43b61d7d24ab8ed8e1ed698 Mon Sep 17 00:00:00 2001 From: scosman Date: Fri, 30 Aug 2024 20:56:23 -0400 Subject: [PATCH 1/2] More idiomatic supabase auth following https://supabase.com/docs/guides/auth/server-side/sveltekit Differences: - only run the auth layout under /account and /login, want fast unauthed marketing pages with no JS - Be even more explicit in maing getSession safe by not calling it on the server at all --- src/app.d.ts | 2 ++ src/hooks.server.ts | 22 +++++++++++++++- src/routes/(admin)/account/+layout.server.ts | 19 ++++---------- src/routes/(admin)/account/+layout.ts | 26 ++++++++++++------- .../(marketing)/login/+layout.server.ts | 4 +-- 5 files changed, 45 insertions(+), 28 deletions(-) diff --git a/src/app.d.ts b/src/app.d.ts index e80cd0ae..a6ae296c 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -13,6 +13,8 @@ declare global { user: User | null amr: AMREntry[] | null }> + session: Session | null + user: User | null } interface PageData { session: Session | null diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 05fd22d4..461d211a 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -7,8 +7,10 @@ import { import { createServerClient } from "@supabase/ssr" import { createClient } from "@supabase/supabase-js" import type { Handle } from "@sveltejs/kit" +import { sequence } from "@sveltejs/kit/hooks" +import { redirect } from "@sveltejs/kit" -export const handle: Handle = async ({ event, resolve }) => { +export const supabase: Handle = async ({ event, resolve }) => { event.locals.supabase = createServerClient( PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, @@ -82,3 +84,21 @@ export const handle: Handle = async ({ event, resolve }) => { }, }) } + +const authGuard: Handle = async ({ event, resolve }) => { + const { session, user } = await event.locals.safeGetSession() + event.locals.session = session + event.locals.user = user + + if (!event.locals.session && event.url.pathname.startsWith("/account")) { + redirect(303, "/login") + } + + if (event.locals.session && event.url.pathname === "/login") { + redirect(303, "/account") + } + + return resolve(event) +} + +export const handle: Handle = sequence(supabase, authGuard) diff --git a/src/routes/(admin)/account/+layout.server.ts b/src/routes/(admin)/account/+layout.server.ts index e778ece8..13000263 100644 --- a/src/routes/(admin)/account/+layout.server.ts +++ b/src/routes/(admin)/account/+layout.server.ts @@ -1,21 +1,12 @@ -import { redirect } from "@sveltejs/kit" import type { LayoutServerLoad } from "./$types" export const load: LayoutServerLoad = async ({ - locals: { supabase, safeGetSession }, + locals: { session }, cookies, }) => { - const { session, user } = await safeGetSession() - - if (!session || !user?.id) { - redirect(303, "/login") + // Session here is from authGuard hook + return { + session, + cookies: cookies.getAll(), } - - const { data: profile } = await supabase - .from("profiles") - .select(`*`) - .eq("id", user.id) - .single() - - return { session, user, profile, cookies: cookies.getAll() } } diff --git a/src/routes/(admin)/account/+layout.ts b/src/routes/(admin)/account/+layout.ts index 01885699..d5ed0e3f 100644 --- a/src/routes/(admin)/account/+layout.ts +++ b/src/routes/(admin)/account/+layout.ts @@ -31,12 +31,16 @@ export const load = async ({ fetch, data, depends, url }) => { }, }) - /** - * Not always safe on server, but calling getUser next to verify JWT token - */ - const { - data: { session }, - } = await supabase.auth.getSession() + // on server populated on server by LayoutData, using authGuard hook + let session = data.session + if (isBrowser()) { + // Only call getSession in browser where it's safe. + const getSessionResponse = await supabase.auth.getSession() + session = getSessionResponse.data.session + } + if (!session) { + redirect(303, "/login") + } // https://github.com/supabase/auth-js/issues/888#issuecomment-2189298518 if ("suppressGetSessionWarning" in supabase.auth) { @@ -53,14 +57,16 @@ export const load = async ({ fetch, data, depends, url }) => { } = await supabase.auth.getUser() if (userError || !user) { // JWT validation has failed - console.log("User error", userError) redirect(303, "/login") } - const { data: aal } = await supabase.auth.mfa.getAuthenticatorAssuranceLevel() + const { data: profile } = await supabase + .from("profiles") + .select(`*`) + .eq("id", user.id) + .single() - const profile: Database["public"]["Tables"]["profiles"]["Row"] | null = - data.profile + const { data: aal } = await supabase.auth.mfa.getAuthenticatorAssuranceLevel() const createProfilePath = "/account/create_profile" const signOutPath = "/account/sign_out" diff --git a/src/routes/(marketing)/login/+layout.server.ts b/src/routes/(marketing)/login/+layout.server.ts index af7d4c4f..603b7fe9 100644 --- a/src/routes/(marketing)/login/+layout.server.ts +++ b/src/routes/(marketing)/login/+layout.server.ts @@ -6,15 +6,13 @@ export const load: LayoutServerLoad = async ({ locals: { safeGetSession }, cookies, }) => { - const { session } = await safeGetSession() - // if the user is already logged in return them to the account page + const { session } = await safeGetSession() if (session) { redirect(303, "/account") } return { - session: session, url: url.origin, cookies: cookies.getAll(), } From 3d0cf7930491feb39209c670dcacabba3e3aab77 Mon Sep 17 00:00:00 2001 From: scosman Date: Fri, 30 Aug 2024 21:11:59 -0400 Subject: [PATCH 2/2] Don't recall safeGetSession, already in the hook --- src/routes/(marketing)/login/+layout.server.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/routes/(marketing)/login/+layout.server.ts b/src/routes/(marketing)/login/+layout.server.ts index 603b7fe9..019d6373 100644 --- a/src/routes/(marketing)/login/+layout.server.ts +++ b/src/routes/(marketing)/login/+layout.server.ts @@ -2,12 +2,11 @@ import { redirect } from "@sveltejs/kit" import type { LayoutServerLoad } from "./$types" export const load: LayoutServerLoad = async ({ - url, - locals: { safeGetSession }, + locals: { session }, cookies, + url, }) => { // if the user is already logged in return them to the account page - const { session } = await safeGetSession() if (session) { redirect(303, "/account") }