From fc4ead6e3094bac23e702c44cc3d41c1ccd39373 Mon Sep 17 00:00:00 2001 From: David Kizivat Date: Thu, 9 May 2024 03:06:56 +0200 Subject: [PATCH 1/7] migrate to new supabase ssr auth package --- package.json | 4 +- src/app.d.ts | 7 ++- src/hooks.server.ts | 55 +++++++++++++++---- .../(admin)/account/(menu)/+page.server.ts | 4 +- .../account/(menu)/billing/+page.server.ts | 10 ++-- .../(menu)/billing/manage/+page.server.ts | 12 ++-- .../account/(menu)/settings/+page.svelte | 4 +- .../(menu)/settings/change_email/+page.svelte | 6 +- .../settings/change_password/+page.svelte | 14 ++--- src/routes/(admin)/account/+layout.server.ts | 6 +- src/routes/(admin)/account/+layout.ts | 43 ++++++++++++--- .../(admin)/account/api/+page.server.ts | 30 +++++----- .../account/create_profile/+page.svelte | 8 +-- .../account/subscribe/[slug]/+page.server.ts | 14 ++--- .../account/subscription_helpers.server.ts | 18 +++--- .../(marketing)/login/+layout.server.ts | 4 +- src/routes/(marketing)/login/+layout.ts | 35 +++++++++--- 17 files changed, 176 insertions(+), 98 deletions(-) diff --git a/package.json b/package.json index 4a0e702e..1b3e85a2 100644 --- a/package.json +++ b/package.json @@ -37,9 +37,9 @@ }, "type": "module", "dependencies": { - "@supabase/auth-helpers-sveltekit": "^0.10.2", "@supabase/auth-ui-svelte": "^0.2.2", - "@supabase/supabase-js": "^2.33.0", + "@supabase/ssr": "^0.3.0", + "@supabase/supabase-js": "^2.43.1", "stripe": "^13.3.0" } } diff --git a/src/app.d.ts b/src/app.d.ts index 76d098d5..d5cb8968 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,4 +1,4 @@ -import { SupabaseClient, Session } from "@supabase/supabase-js" +import { Session, SupabaseClient } from "@supabase/supabase-js" import { Database } from "./DatabaseDefinitions" // See https://kit.svelte.dev/docs/types#app @@ -8,7 +8,10 @@ declare global { interface Locals { supabase: SupabaseClient supabaseServiceRole: SupabaseClient - getSession(): Promise + safeGetSession: () => Promise<{ + session: Session | null + user: User | null + }> } interface PageData { session: Session | null diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 76c2b8a7..00bf7ec1 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,19 +1,35 @@ // src/hooks.server.ts +import { PRIVATE_SUPABASE_SERVICE_ROLE } from "$env/static/private" import { - PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, + PUBLIC_SUPABASE_URL, } from "$env/static/public" -import { PRIVATE_SUPABASE_SERVICE_ROLE } from "$env/static/private" -import { createSupabaseServerClient } from "@supabase/auth-helpers-sveltekit" +import { createServerClient } from "@supabase/ssr" import { createClient } from "@supabase/supabase-js" import type { Handle } from "@sveltejs/kit" export const handle: Handle = async ({ event, resolve }) => { - event.locals.supabase = createSupabaseServerClient({ - supabaseUrl: PUBLIC_SUPABASE_URL, - supabaseKey: PUBLIC_SUPABASE_ANON_KEY, - event, - }) + event.locals.supabase = createServerClient( + PUBLIC_SUPABASE_URL, + PUBLIC_SUPABASE_ANON_KEY, + { + cookies: { + get: (key) => event.cookies.get(key), + /** + * Note: You have to add the `path` variable to the + * set and remove method due to sveltekit's cookie API + * requiring this to be set, setting the path to an empty string + * will replicate previous/standard behaviour (https://kit.svelte.dev/docs/types#public-types-cookies) + */ + set: (key, value, options) => { + event.cookies.set(key, value, { ...options, path: "/" }) + }, + remove: (key, options) => { + event.cookies.delete(key, { ...options, path: "/" }) + }, + }, + }, + ) event.locals.supabaseServiceRole = createClient( PUBLIC_SUPABASE_URL, @@ -22,18 +38,33 @@ export const handle: Handle = async ({ event, resolve }) => { ) /** - * A convenience helper so we can just call await getSession() instead const { data: { session } } = await supabase.auth.getSession() + * Unlike `supabase.auth.getSession()`, which returns the session _without_ + * validating the JWT, this function also calls `getUser()` to validate the + * JWT before returning the session. */ - event.locals.getSession = async () => { + event.locals.safeGetSession = async () => { const { data: { session }, } = await event.locals.supabase.auth.getSession() - return session + if (!session) { + return { session: null, user: null } + } + + const { + data: { user }, + error, + } = await event.locals.supabase.auth.getUser() + if (error) { + // JWT validation has failed + return { session: null, user: null } + } + + return { session, user } } return resolve(event, { filterSerializedResponseHeaders(name) { - return name === "content-range" + return name === "content-range" || name === "x-supabase-api-version" }, }) } diff --git a/src/routes/(admin)/account/(menu)/+page.server.ts b/src/routes/(admin)/account/(menu)/+page.server.ts index 0ed68585..1ff77309 100644 --- a/src/routes/(admin)/account/(menu)/+page.server.ts +++ b/src/routes/(admin)/account/(menu)/+page.server.ts @@ -1,8 +1,8 @@ import { redirect } from "@sveltejs/kit" export const actions = { - signout: async ({ locals: { supabase, getSession } }) => { - const session = await getSession() + signout: async ({ locals: { supabase, safeGetSession } }) => { + const { session } = await safeGetSession() if (session) { await supabase.auth.signOut() throw redirect(303, "/") diff --git a/src/routes/(admin)/account/(menu)/billing/+page.server.ts b/src/routes/(admin)/account/(menu)/billing/+page.server.ts index bc151b1b..ec178997 100644 --- a/src/routes/(admin)/account/(menu)/billing/+page.server.ts +++ b/src/routes/(admin)/account/(menu)/billing/+page.server.ts @@ -1,21 +1,21 @@ -import { redirect, error } from "@sveltejs/kit" +import { error, redirect } from "@sveltejs/kit" import { - getOrCreateCustomerId, fetchSubscription, + getOrCreateCustomerId, } from "../../subscription_helpers.server" import type { PageServerLoad } from "./$types" export const load: PageServerLoad = async ({ - locals: { getSession, supabaseServiceRole }, + locals: { safeGetSession, supabaseServiceRole }, }) => { - const session = await getSession() + const { session, user } = await safeGetSession() if (!session) { throw redirect(303, "/login") } const { error: idError, customerId } = await getOrCreateCustomerId({ supabaseServiceRole, - session, + user, }) if (idError || !customerId) { throw error(500, { diff --git a/src/routes/(admin)/account/(menu)/billing/manage/+page.server.ts b/src/routes/(admin)/account/(menu)/billing/manage/+page.server.ts index b8e0485d..0575c6e6 100644 --- a/src/routes/(admin)/account/(menu)/billing/manage/+page.server.ts +++ b/src/routes/(admin)/account/(menu)/billing/manage/+page.server.ts @@ -1,22 +1,22 @@ -import { redirect, error } from "@sveltejs/kit" -import { getOrCreateCustomerId } from "../../../subscription_helpers.server" -import type { PageServerLoad } from "./$types" import { PRIVATE_STRIPE_API_KEY } from "$env/static/private" +import { error, redirect } from "@sveltejs/kit" import Stripe from "stripe" +import { getOrCreateCustomerId } from "../../../subscription_helpers.server" +import type { PageServerLoad } from "./$types" const stripe = new Stripe(PRIVATE_STRIPE_API_KEY, { apiVersion: "2023-08-16" }) export const load: PageServerLoad = async ({ url, - locals: { getSession, supabaseServiceRole }, + locals: { safeGetSession, supabaseServiceRole }, }) => { - const session = await getSession() + const { session, user } = await safeGetSession() if (!session) { throw redirect(303, "/login") } const { error: idError, customerId } = await getOrCreateCustomerId({ supabaseServiceRole, - session, + user, }) if (idError || !customerId) { throw error(500, { diff --git a/src/routes/(admin)/account/(menu)/settings/+page.svelte b/src/routes/(admin)/account/(menu)/settings/+page.svelte index c096ed73..5914c9bf 100644 --- a/src/routes/(admin)/account/(menu)/settings/+page.svelte +++ b/src/routes/(admin)/account/(menu)/settings/+page.svelte @@ -7,7 +7,7 @@ adminSection.set("settings") export let data - let { session, profile } = data + let { profile, user } = data @@ -39,7 +39,7 @@ diff --git a/src/routes/(admin)/account/(menu)/settings/change_email/+page.svelte b/src/routes/(admin)/account/(menu)/settings/change_email/+page.svelte index 1bd0acb9..bf6e47a5 100644 --- a/src/routes/(admin)/account/(menu)/settings/change_email/+page.svelte +++ b/src/routes/(admin)/account/(menu)/settings/change_email/+page.svelte @@ -1,14 +1,14 @@ @@ -27,7 +27,7 @@ { id: "email", label: "Email", - initialValue: session?.user?.email ?? "", + initialValue: user?.email ?? "", placeholder: "Email address", }, ]} diff --git a/src/routes/(admin)/account/(menu)/settings/change_password/+page.svelte b/src/routes/(admin)/account/(menu)/settings/change_password/+page.svelte index fef0fb1b..e87ebf14 100644 --- a/src/routes/(admin)/account/(menu)/settings/change_password/+page.svelte +++ b/src/routes/(admin)/account/(menu)/settings/change_password/+page.svelte @@ -8,27 +8,25 @@ adminSection.set("settings") export let data - let { session, supabase } = data + let { user, supabase } = data // True if definitely has a password, but can be false if they // logged in with oAuth or email link // @ts-expect-error: we ignore because Supabase does not maintain an AMR typedef - let hasPassword = session?.user?.amr?.find((x) => x.method === "password") + let hasPassword = user?.amr?.find((x) => x.method === "password") ? true : false // @ts-expect-error: we ignore because Supabase does not maintain an AMR typedef - let usingOAuth = session?.user?.amr?.find((x) => x.method === "oauth") - ? true - : false + let usingOAuth = user?.amr?.find((x) => x.method === "oauth") ? true : false let sendBtn: HTMLButtonElement let sentEmail = false let sendForgotPassword = () => { sendBtn.disabled = true sendBtn.textContent = "Sending..." - let email = session?.user.email + let email = user?.email if (email) { supabase.auth .resetPasswordForEmail(email, { @@ -91,8 +89,8 @@
Change Password By Email
{/if}
- The button below will send you an email at {session?.user?.email} which will - allow you to set your password. + The button below will send you an email at {user?.email} which will allow + you to set your password.
- {:else} + {:else if editButtonTitle && editLink}