diff --git a/src/app.d.ts b/src/app.d.ts index d5cb8968..e80cd0ae 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,4 +1,4 @@ -import { Session, SupabaseClient } from "@supabase/supabase-js" +import { Session, SupabaseClient, type AMREntry } from "@supabase/supabase-js" import { Database } from "./DatabaseDefinitions" // See https://kit.svelte.dev/docs/types#app @@ -11,6 +11,7 @@ declare global { safeGetSession: () => Promise<{ session: Session | null user: User | null + amr: AMREntry[] | null }> } interface PageData { diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 00bf7ec1..0cfd821f 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -47,19 +47,25 @@ export const handle: Handle = async ({ event, resolve }) => { data: { session }, } = await event.locals.supabase.auth.getSession() if (!session) { - return { session: null, user: null } + return { session: null, user: null, amr: null } } const { data: { user }, - error, + error: userError, } = await event.locals.supabase.auth.getUser() - if (error) { + if (userError) { // JWT validation has failed - return { session: null, user: null } + return { session: null, user: null, amr: null } } - return { session, user } + const { data: aal, error: amrError } = + await event.locals.supabase.auth.mfa.getAuthenticatorAssuranceLevel() + if (amrError) { + return { session, user, amr: null } + } + + return { session, user, amr: aal.currentAuthenticationMethods } } return resolve(event, { diff --git a/src/routes/(admin)/account/(menu)/billing/+page.server.ts b/src/routes/(admin)/account/(menu)/billing/+page.server.ts index ec178997..6cdd19b3 100644 --- a/src/routes/(admin)/account/(menu)/billing/+page.server.ts +++ b/src/routes/(admin)/account/(menu)/billing/+page.server.ts @@ -9,7 +9,7 @@ export const load: PageServerLoad = async ({ locals: { safeGetSession, supabaseServiceRole }, }) => { const { session, user } = await safeGetSession() - if (!session) { + if (!session || !user?.id) { throw redirect(303, "/login") } diff --git a/src/routes/(admin)/account/+layout.server.ts b/src/routes/(admin)/account/+layout.server.ts index 4fa70ea3..e91f6932 100644 --- a/src/routes/(admin)/account/+layout.server.ts +++ b/src/routes/(admin)/account/+layout.server.ts @@ -6,14 +6,14 @@ export const load: LayoutServerLoad = async ({ }) => { const { session, user } = await safeGetSession() - if (!session) { + if (!session || !user?.id) { throw redirect(303, "/login") } const { data: profile } = await supabase .from("profiles") .select(`*`) - .eq("id", user.id) + .eq("id", user?.id) .single() return { session, profile } diff --git a/src/routes/(admin)/account/+layout.ts b/src/routes/(admin)/account/+layout.ts index 718016e6..0355cc59 100644 --- a/src/routes/(admin)/account/+layout.ts +++ b/src/routes/(admin)/account/+layout.ts @@ -45,6 +45,8 @@ export const load = async ({ fetch, data, depends, url }) => { data: { user }, } = await supabase.auth.getUser() + const { data: aal } = await supabase.auth.mfa.getAuthenticatorAssuranceLevel() + const profile: Database["public"]["Tables"]["profiles"]["Row"] | null = data.profile @@ -57,7 +59,13 @@ export const load = async ({ fetch, data, depends, url }) => { throw redirect(303, createProfilePath) } - return { supabase, session, profile, user } + return { + supabase, + session, + profile, + user, + amr: aal?.currentAuthenticationMethods, + } } export const _hasFullProfile = ( diff --git a/src/routes/(admin)/account/api/+page.server.ts b/src/routes/(admin)/account/api/+page.server.ts index 85b12363..bdda9286 100644 --- a/src/routes/(admin)/account/api/+page.server.ts +++ b/src/routes/(admin)/account/api/+page.server.ts @@ -44,7 +44,7 @@ export const actions = { } }, updatePassword: async ({ request, locals: { supabase, safeGetSession } }) => { - const { session, user } = await safeGetSession() + const { session, user, amr } = await safeGetSession() if (!session) { throw redirect(303, "/login") } @@ -56,8 +56,7 @@ export const actions = { // Can check if we're a "password recovery" session by checking session amr // let currentPassword take priority if provided (user can use either form) - // @ts-expect-error: we ignore because Supabase does not maintain an AMR typedef - const recoveryAmr = user?.amr?.find((x) => x.method === "recovery") + const recoveryAmr = amr?.find((x) => x.method === "recovery") const isRecoverySession = recoveryAmr && !currentPassword // if this is password recovery session, check timestamp of recovery session @@ -151,7 +150,7 @@ export const actions = { locals: { supabase, supabaseServiceRole, safeGetSession }, }) => { const { session, user } = await safeGetSession() - if (!session) { + if (!session || !user?.id) { throw redirect(303, "/login") } @@ -193,7 +192,7 @@ export const actions = { }, updateProfile: async ({ request, locals: { supabase, safeGetSession } }) => { const { session, user } = await safeGetSession() - if (!session) { + if (!session || !user?.id) { throw redirect(303, "/login") } @@ -239,7 +238,7 @@ export const actions = { } const { error } = await supabase.from("profiles").upsert({ - id: user?.id, + id: user.id, full_name: fullName, company_name: companyName, website: website,