Update guide to include PKCE auth flow

This commit is contained in:
Andrew Smith committed 2023-06-05 10:58:07 +00:00
1 parent 1d5a53e8fd
commit 8dfd621c60
1 file changed
+572 -187
@@ -9,7 +9,9 @@ export const meta = {
This submodule provides convenience helpers for implementing user authentication in [SvelteKit](https://kit.svelte.dev/) applications.
## Installation
## Configuration
### Install SvelteKit Auth Helpers library
This library supports Node.js `^16.15.0`.
@@ -17,21 +19,65 @@ This library supports Node.js `^16.15.0`.
npm install @supabase/auth-helpers-sveltekit
```
## Getting Started
### Declare Environment Variables
### Configuration
Retrieve your project's URL and anon key from your [API settings](https://app.supabase.com/project/_/settings/api), and create a `.env.local` file with the following environment variables:
Set up the following env vars. For local development you can set them in a `.env` file. See an [example](https://github.com/supabase/auth-helpers/blob/main/examples/sveltekit/.env.example).
```bash
```bash title=".env.local"
# Find these in your Supabase project settings https://app.supabase.com/project/_/settings/api
PUBLIC_SUPABASE_URL=https://your-project.supabase.co
PUBLIC_SUPABASE_ANON_KEY=your-anon-key
```
### Set up the Supabase client
### Creating a Supabase Client
Create a server supabase client in a handle hook:
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
Create a new `hooks.server.js` file in the root of your project and populate with the following:
```js title=src/hooks.server.js
// src/hooks.server.js
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
export const handle = async ({ event, resolve }) => {
event.locals.supabase = createSupabaseServerClient({
supabaseUrl: PUBLIC_SUPABASE_URL,
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
event,
})
/**
* a little helper that is written for convenience so that instead
* of calling `const { data: { session } } = await supabase.auth.getSession()`
* you just call this `await getSession()`
*/
event.locals.getSession = async () => {
const {
data: { session },
} = await event.locals.supabase.auth.getSession()
return session
}
return resolve(event, {
filterSerializedResponseHeaders(name) {
return name === 'content-range'
},
})
}
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
Create a new `hooks.server.ts` file in the root of your project and populate with the following:
```ts title=src/hooks.server.ts
// src/hooks.server.ts
@@ -59,11 +105,6 @@ export const handle: Handle = async ({ event, resolve }) => {
}
return resolve(event, {
/**
* There´s an issue with `filterSerializedResponseHeaders` not working when using `sequence`
*
* https://github.com/sveltejs/kit/issues/8061
*/
filterSerializedResponseHeaders(name) {
return name === 'content-range'
},
@@ -71,90 +112,65 @@ export const handle: Handle = async ({ event, resolve }) => {
}
```
> Note that we are specifying filterSerializedResponseHeaders here. We need to tell SvelteKit that supabase needs the content-range header.
</TabPanel>
</Tabs>
### Send session to client
<Admonition type="info">
In order to make the session available to the UI (pages, layouts) we need to pass the session in the root layout server load function:
Note that we are specifying filterSerializedResponseHeaders here. We need to tell SvelteKit that supabase needs the content-range header.
```ts title=src/routes/+layout.server.ts
// src/routes/+layout.server.ts
import type { LayoutServerLoad } from './$types'
</Admonition>
export const load: LayoutServerLoad = async ({ locals: { getSession } }) => {
return {
session: await getSession(),
### Code Exchange Route
The `Code Exchange` route is required for the [server-side auth flow](https://supabase.com/docs/guides/auth/server-side-rendering) implemented by the SvelteKit Auth Helpers. It exchanges an auth `code` for the user's `session`, which is set as a cookie for future requests made to Supabase.
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
Create a new file at `src/routes/auth/callback/+server.js` and populate with the following:
```jsx title="src/routes/auth/callback/+server.js"
import { redirect } from '@sveltejs/kit'
export const GET = async ({ url, locals: { supabase } }) => {
const code = url.searchParams.get('code')
if (code) {
await supabase.auth.exchangeCodeForSession(code)
}
throw redirect(303, '/')
}
```
### Shared Load functions and pages
</TabPanel>
To be able to use Supabase in shared load functions and inside pages you need to create a Supabase client in the root layout load:
<TabPanel id="ts" label="TypeScript">
```ts
// src/routes/+layout.ts
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public'
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit'
import type { LayoutLoad } from './$types'
import type { Database } from '../DatabaseDefinitions'
Create a new file at `src/routes/auth/callback/+server.ts` and populate with the following:
export const load: LayoutLoad = async ({ fetch, data, depends }) => {
depends('supabase:auth')
```tsx title="src/routes/auth/callback/+server.ts"
import { redirect } from '@sveltejs/kit'
const supabase = createSupabaseLoadClient<Database>({
supabaseUrl: PUBLIC_SUPABASE_URL,
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
event: { fetch },
serverSession: data.session,
})
export const GET = async ({ url, locals: { supabase } }) => {
const code = url.searchParams.get('code')
const {
data: { session },
} = await supabase.auth.getSession()
if (code) {
await supabase.auth.exchangeCodeForSession(code)
}
return { supabase, session }
throw redirect(303, '/')
}
```
Access the client inside pages by `$page.data.supabase` or `data.supabase` when using `export let data: PageData`.
The usage of `depends` tells sveltekit that this load function should be executed whenever `invalidate` is called to keep the page store in sync.
`createSupabaseLoadClient` caches the client when running in a browser environment and therefore does not create a new client for every time the load function runs.
### Setting up the event listener on the client side
We need to create an event listener in the root `+layout.svelte` file in order catch supabase events being triggered.
```svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { invalidate } from '$app/navigation';
import { onMount } from 'svelte';
import type { LayoutData } from './$types';
export let data: LayoutData;
$: ({ supabase, session } = data);
onMount(() => {
const {
data: { subscription },
} = supabase.auth.onAuthStateChange((event, _session) => {
if (_session?.expires_at !== session?.expires_at) {
invalidate('supabase:auth');
}
});
return () => subscription.unsubscribe();
});
</script>
<slot />
```
The usage of `invalidate` tells sveltekit that the root `+layout.ts` load function should be executed whenever the session updates to keep the page store in sync.
</TabPanel>
</Tabs>
### Generate types from your database
@@ -181,78 +197,323 @@ declare global {
}
```
## Client-side data fetching with RLS
## Authentication
For [row level security](https://supabase.com/docs/guides/auth/row-level-security) to work properly when fetching data client-side, you need to use `supabaseClient` from `PageData` and only run your query once the session is defined client-side:
Authentication can be initiated [client](/docs/guides/auth/auth-helpers/sveltekit#client-side) or [server-side](/docs/guides/auth/auth-helpers/sveltekit#server-side). All of the [supabase-js authentication strategies](http://localhost:3001/docs/reference/javascript/auth-api) are supported with the Auth Helpers client.
```html
<script lang="ts">
import type { PageData } from './$types'
<Admonition type="info">
export let data: PageData
Note: The authentication flow requires the [Code Exchange Route](/docs/guides/auth/auth-helpers/sveltekit#code-exchange-route) to exchange a `code` for the user's `session`.
let loadedData = []
async function loadData() {
const { data: result } = await data.supabase.from('test').select('*').limit(20)
loadedData = result
}
</Admonition>
$: if (data.session) {
loadData()
}
</script>
### Client-side
{#if data.session}
<p>client-side data fetching with RLS</p>
<pre>{JSON.stringify(loadedData, null, 2)}</pre>
{/if}
```
#### Send session to client
## Server-side data fetching with RLS
To make the session available across the UI, including pages and layouts, it is crucial to pass the session as a parameter in the root layout's server load function.
```html
<!-- src/routes/profile/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types'
export let data: PageData
$: ({ user, tableData } = data)
</script>
<div>Protected content for {user.email}</div>
<pre>{JSON.stringify(tableData, null, 2)}</pre>
<pre>{JSON.stringify(user, null, 2)}</pre>
```
```ts
// src/routes/profile/+page.ts
import type { PageLoad } from './$types'
import { redirect } from '@sveltejs/kit'
export const load: PageLoad = async ({ parent }) => {
const { supabase, session } = await parent()
if (!session) {
throw redirect(303, '/')
}
const { data: tableData } = await supabase.from('test').select('*')
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
```js title=src/routes/+layout.server.js
// src/routes/+layout.server.js
export const load = async ({ locals: { getSession } }) => {
return {
user: session.user,
tableData,
session: await getSession(),
}
}
```
## Protecting API routes
</TabPanel>
<TabPanel id="ts" label="TypeScript">
```ts title=src/routes/+layout.server.ts
// src/routes/+layout.server.ts
export const load = async ({ locals: { getSession } }) => {
return {
session: await getSession(),
}
}
```
</TabPanel>
</Tabs>
#### Shared Load functions and pages
To utilize Supabase in shared load functions and within pages, it is essential to create a Supabase client in the root layout load.
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
```ts title="src/routes/+layout.js"
// src/routes/+layout.js
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public'
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit'
export const load = async ({ fetch, data, depends }) => {
depends('supabase:auth')
const supabase = createSupabaseLoadClient({
supabaseUrl: PUBLIC_SUPABASE_URL,
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
event: { fetch },
serverSession: data.session,
})
const {
data: { session },
} = await supabase.auth.getSession()
return { supabase, session }
}
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
```ts title="src/routes/+layout.ts"
// src/routes/+layout.ts
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public'
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit'
import type { Database } from '../DatabaseDefinitions'
export const load = async ({ fetch, data, depends }) => {
depends('supabase:auth')
const supabase = createSupabaseLoadClient<Database>({
supabaseUrl: PUBLIC_SUPABASE_URL,
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
event: { fetch },
serverSession: data.session,
})
const {
data: { session },
} = await supabase.auth.getSession()
return { supabase, session }
}
```
<Admonition type="info">
TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createSupabaseLoadClient` to add type support to the Supabase client.
</Admonition>
</TabPanel>
</Tabs>
Access the client inside pages by `$page.data.supabase` or `data.supabase` when using `export let data: PageData`.
The usage of `depends` tells sveltekit that this load function should be executed whenever `invalidate` is called to keep the page store in sync.
`createSupabaseLoadClient` caches the client when running in a browser environment and therefore does not create a new client for every time the load function runs.
#### Setting up the event listener on the client side
We need to create an event listener in the root `+layout.svelte` file in order to catch supabase events being triggered.
```svelte title="src/routes/+layout.svelte"
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { invalidate } from '$app/navigation'
import { onMount } from 'svelte'
export let data
$: ({ supabase, session } = data)
onMount(() => {
const {
data: { subscription },
} = supabase.auth.onAuthStateChange((event, _session) => {
if (_session?.expires_at !== session?.expires_at) {
invalidate('supabase:auth')
}
})
return () => subscription.unsubscribe()
});
</script>
<slot />
```
The usage of `invalidate` tells SvelteKit that the root `+layout.ts` load function should be executed whenever the session updates to keep the page store in sync.
#### Sign in / Sign up / Sign out
We can access the supabase instance in our `+page.svelte` file through the data object.
```svelte title="src/routes/auth/+page.svelte"
<!-- // src/routes/auth/+page.svelte -->
<script>
let { supabase } = data
$: ({ supabase } = data)
let email
let password
const handleSignUp = async () => {
await supabase.auth.signUp({
email,
password,
options: {
emailRedirectTo: `${location.origin}/auth/callback`,
},
})
}
const handleSignIn = async () => {
await supabase.auth.signInWithPassword({
email,
password,
})
}
const handleSignOut = async () => {
await supabase.auth.signOut()
}
</script>
<form on:submit="{handleSignUp}">
<input name="email" bind:value="{email}" />
<input type="password" name="password" bind:value="{password}" />
<button>Sign up</button>
</form>
<button on:click="{handleSignIn}">Sign in</button>
<button on:click="{handleSignOut}">Sign out</button>
```
### Server-side
[Form Actions](https://kit.svelte.dev/docs/form-actions) can be used to trigger the authentication process from form submissions.
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
```js title="src/routes/login/+page.server.js"
// src/routes/login/+page.server.js
export const actions = {
default: async ({ request, url, locals: { supabase } }) => {
const formData = await request.formData()
const email = formData.get('email')
const password = formData.get('password')
const { error } = await supabase.auth.signUp({
email,
password,
options: {
emailRedirectTo: `${url.origin}/auth/callback`,
},
})
if (error) {
return fail(500, { message: 'Server error. Try again later.', success: false, email })
}
return {
message: 'Please check your email for a magic link to log into the website.',
success: true,
}
},
}
```
```svelte title="src/routes/login/+page.svelte"
<!-- // src/routes/login/+page.svelte -->
<script>
import { enhance } from '$app/forms'
export let form
</script>
<form method="post" use:enhance>
<input name="email" value={form?.email ?? ''} />
<input type="password" name="password" />
<button>Sign up</button>
</form>
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
```js title="src/routes/login/+page.server.ts"
// src/routes/login/+page.server.ts
export const actions = {
default: async ({ request, url, locals: { supabase } }) => {
const formData = await request.formData()
const email = formData.get('email') as string
const password = formData.get('password') as string
const { error } = await supabase.auth.signUp({
email,
password,
options: {
emailRedirectTo: `${url.origin}/auth/callback`,
},
})
if (error) {
return fail(500, { message: 'Server error. Try again later.', success: false, email })
}
return {
message: 'Please check your email for a magic link to log into the website.',
success: true,
}
},
}
```
```svelte title="src/routes/login/+page.svelte"
<!-- // src/routes/login/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms'
export let form
</script>
<form method="post" use:enhance>
<input name="email" value={form?.email ?? ''} />
<input type="password" name="password" />
<button>Sign up</button>
</form>
```
</TabPanel>
</Tabs>
## Authorization
### Protecting API routes
Wrap an API Route to check that the user has a valid session. If they're not logged in the session is `null`.
```ts
// src/routes/api/protected-route/+server.ts
import type { RequestHandler } from './$types'
import { json, error } from '@sveltejs/kit'
export const GET: RequestHandler = async ({ locals: { supabase, getSession } }) => {
export const GET = async ({ locals: { supabase, getSession } }) => {
const session = await getSession()
if (!session) {
// the user is not signed in
@@ -266,16 +527,15 @@ export const GET: RequestHandler = async ({ locals: { supabase, getSession } })
If you visit `/api/protected-route` without a valid session cookie, you will get a 401 response.
## Protecting Actions
### Protecting Actions
Wrap an Action to check that the user has a valid session. If they're not logged in the session is `null`.
```ts
// src/routes/posts/+page.server.ts
import type { Actions } from './$types'
import { error, fail } from '@sveltejs/kit'
export const actions: Actions = {
export const actions = {
createPost: async ({ request, locals: { supabase, getSession } }) => {
const session = await getSession()
@@ -305,14 +565,148 @@ export const actions: Actions = {
If you try to submit a form with the action `?/createPost` without a valid session cookie, you will get a 401 error response.
### Protecting multiple routes
To avoid writing the same auth logic in every single route you can use the handle hook to
protect multiple routes at once.
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
>
<TabPanel id="js" label="JavaScript">
```js
// src/hooks.server.js
import { redirect, error } from '@sveltejs/kit'
export const handle = async ({ event, resolve }) => {
// protect requests to all routes that start with /protected-routes
if (event.url.pathname.startsWith('/protected-routes')) {
const session = await event.locals.getSession()
if (!session) {
// the user is not signed in
throw redirect(303, '/')
}
}
// protect POST requests to all routes that start with /protected-posts
if (event.url.pathname.startsWith('/protected-posts') && event.request.method === 'POST') {
const session = await event.locals.getSession()
if (!session) {
// the user is not signed in
throw error(303, '/')
}
}
return resolve(event)
}
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
```ts
// src/hooks.server.ts
import { type Handle, redirect, error } from '@sveltejs/kit'
export const handle: Handle = async ({ event, resolve }) => {
// protect requests to all routes that start with /protected-routes
if (event.url.pathname.startsWith('/protected-routes')) {
const session = await event.locals.getSession()
if (!session) {
// the user is not signed in
throw redirect(303, '/')
}
}
// protect POST requests to all routes that start with /protected-posts
if (event.url.pathname.startsWith('/protected-posts') && event.request.method === 'POST') {
const session = await event.locals.getSession()
if (!session) {
// the user is not signed in
throw error(303, '/')
}
}
return resolve(event)
}
```
</TabPanel>
</Tabs>
## Data fetching
### Client-side data fetching with RLS
For [row level security](https://supabase.com/docs/guides/auth/row-level-security) to work properly when fetching data client-side, you need to use `supabaseClient` from `PageData` and only run your query once the session is defined client-side:
```html
<script lang="ts">
export let data
let loadedData = []
async function loadData() {
const { data: result } = await data.supabase.from('test').select('*').limit(20)
loadedData = result
}
$: if (data.session) {
loadData()
}
</script>
{#if data.session}
<p>client-side data fetching with RLS</p>
<pre>{JSON.stringify(loadedData, null, 2)}</pre>
{/if}
```
### Server-side data fetching with RLS
```html
<!-- src/routes/profile/+page.svelte -->
<script lang="ts">
export let data
let { user, tableData } = data
$: ({ user, tableData } = data)
</script>
<div>Protected content for {user.email}</div>
<pre>{JSON.stringify(tableData, null, 2)}</pre>
<pre>{JSON.stringify(user, null, 2)}</pre>
```
```ts
// src/routes/profile/+page.ts
import { redirect } from '@sveltejs/kit'
export const load = async ({ parent }) => {
const { supabase, session } = await parent()
if (!session) {
throw redirect(303, '/')
}
const { data: tableData } = await supabase.from('test').select('*')
return {
user: session.user,
tableData,
}
}
```
## Saving and deleting the session
```ts
import type { Actions } from './$types'
import { fail, redirect } from '@sveltejs/kit'
import { AuthApiError } from '@supabase/supabase-js'
export const actions: Actions = {
export const actions = {
signin: async ({ request, locals: { supabase } }) => {
const formData = await request.formData()
@@ -351,42 +745,33 @@ export const actions: Actions = {
}
```
## Protecting multiple routes
## Migration Guide [#migration]
To avoid writing the same auth logic in every single route you can use the handle hook to
protect multiple routes at once.
### Migrate to 0.10
#### PKCE Auth Flow
Proof Key for Code Exchange (PKCE) is the new server-side auth flow implemented by the SvelteKit Auth Helpers. It requires a server endpoint for `/auth/callback` that exchanges an auth `code` for the user's `session`.
Check the [Code Exchange Route steps](/docs/guides/auth/auth-helpers/sveltekit#code-exchange-route) above to implement this server endpoint.
#### Authentication
For authentication methods that have a `redirectTo` or `emailRedirectTo`, this must be set to this new code exchange route handler - `/auth/callback`. This is an example with the `signUp` function:
```ts
// src/hooks.server.ts
import type { RequestHandler } from './$types'
import { redirect, error } from '@sveltejs/kit'
export const handle: Handle = async ({ event, resolve }) => {
// protect requests to all routes that start with /protected-routes
if (event.url.pathname.startsWith('/protected-routes')) {
const session = await event.locals.getSession()
if (!session) {
// the user is not signed in
throw redirect(303, '/')
}
}
// protect POST requests to all routes that start with /protected-posts
if (event.url.pathname.startsWith('/protected-posts') && event.request.method === 'POST') {
const session = await event.locals.getSession()
if (!session) {
// the user is not signed in
throw error(303, '/')
}
}
return resolve(event)
}
await supabase.auth.signUp({
email: 'jon@example.com',
password: 'sup3rs3cur3',
options: {
emailRedirectTo: 'http://localhost:3000/auth/callback',
},
})
```
## Migrate from 0.8.x to 0.9 [#migration]
### Migrate from 0.8.x to 0.9 [#migration-0-9]
### Set up the Supabase client [#migration-set-up-supabase-client]
#### Set up the Supabase client [#migration-set-up-supabase-client]
In version 0.9 we now setup our Supabase client for the server inside of a `hooks.server.ts` file.
@@ -448,7 +833,7 @@ export const handle: Handle = async ({ event, resolve }) => {
</TabPanel>
</Tabs>
### Initialize the client [#migration-initialize-client]
#### Initialize the client [#migration-initialize-client]
In order to use the Supabase library in your client code you will need to setup a shared load function inside the root `+layout.ts` and create a `+layout.svelte` to handle our event listening for Auth events.
@@ -542,11 +927,11 @@ export const load: LayoutLoad = async ({ fetch, data, depends }) => {
</TabPanel>
</Tabs>
### Set up hooks [#migration-set-up-hooks]
#### Set up hooks [#migration-set-up-hooks]
Since version 0.9 relies on `hooks.server.ts` to setup our client, we no longer need the `hooks.client.ts` in our project for Supabase related code.
### Typings [#migration-typings]
#### Typings [#migration-typings]
<Tabs
scrollable
@@ -604,7 +989,7 @@ declare global {
</TabPanel>
</Tabs>
### Protecting a page [#migration-protecting-a-page]
#### Protecting a page [#migration-protecting-a-page]
<Tabs
scrollable
@@ -686,7 +1071,7 @@ export const load: PageLoad = async ({ parent }) => {
</TabPanel>
</Tabs>
### Protecting a API route [#migration-protecting-a-api-route]
#### Protecting a API route [#migration-protecting-a-api-route]
<Tabs
scrollable
@@ -736,9 +1121,9 @@ export const GET: RequestHandler = async ({ locals: { supabase, getSession } })
</TabPanel>
</Tabs>
## Migrate from 0.7.x to 0.8 [#migration-0-8]
### Migrate from 0.7.x to 0.8 [#migration-0-8]
### Set up the Supabase client [#migration-set-up-supabase-client-0-8]
#### Set up the Supabase client [#migration-set-up-supabase-client-0-8]
<Tabs
scrollable
@@ -786,7 +1171,7 @@ export const supabaseClient = createClient(env.PUBLIC_SUPABASE_URL, env.PUBLIC_S
</TabPanel>
</Tabs>
### Initialize the client [#migration-initialize-client-0-8]
#### Initialize the client [#migration-initialize-client-0-8]
<Tabs
scrollable
@@ -842,7 +1227,7 @@ export const supabaseClient = createClient(env.PUBLIC_SUPABASE_URL, env.PUBLIC_S
</TabPanel>
</Tabs>
### Set up hooks [#migration-set-up-hooks-0-8]
#### Set up hooks [#migration-set-up-hooks-0-8]
<Tabs
scrollable
@@ -889,7 +1274,7 @@ import '$lib/db'
</TabPanel>
</Tabs>
### Typings [#migration-typings-0-8]
#### Typings [#migration-typings-0-8]
<Tabs
scrollable
@@ -946,7 +1331,7 @@ declare namespace App {
</TabPanel>
</Tabs>
### withPageAuth [#migration-with-page-auth-0-8]
#### withPageAuth [#migration-with-page-auth-0-8]
<Tabs
scrollable
@@ -1024,7 +1409,7 @@ export const load: PageLoad = async (event) => {
</TabPanel>
</Tabs>
### withApiAuth [#migration-with-api-auth-0-8]
#### withApiAuth [#migration-with-api-auth-0-8]
<Tabs
scrollable
@@ -1077,15 +1462,15 @@ export const GET: RequestHandler = async (event) => {
</TabPanel>
</Tabs>
## Migrate from 0.6.11 and below to 0.7.0 [#migration-0-7]
### Migrate from 0.6.11 and below to 0.7.0 [#migration-0-7]
There are numerous breaking changes in the latest 0.7.0 version of this library.
### Environment variable prefix
#### Environment variable prefix
The environment variable prefix is now `PUBLIC_` instead of `VITE_` (e.g., `VITE_SUPABASE_URL` is now `PUBLIC_SUPABASE_URL`).
### Set up the Supabase client [#migration-set-up-supabase-client-0-7]
#### Set up the Supabase client [#migration-set-up-supabase-client-0-7]
<Tabs
scrollable
@@ -1134,7 +1519,7 @@ setupSupabaseHelpers({
</TabPanel>
</Tabs>
### Initialize the client [#migration-initialize-client-0-7]
#### Initialize the client [#migration-initialize-client-0-7]
<Tabs
scrollable
@@ -1182,7 +1567,7 @@ The `@supabase/auth-helpers-svelte` library is no longer required as the `@supab
</TabPanel>
</Tabs>
### Set up hooks [#migration-set-up-hooks-0-7]
#### Set up hooks [#migration-set-up-hooks-0-7]
<Tabs
scrollable
@@ -1236,7 +1621,7 @@ export const handle = sequence(auth(), yourHandler)
</TabPanel>
</Tabs>
### Typings [#migration-typings-0-7]
#### Typings [#migration-typings-0-7]
<Tabs
scrollable
@@ -1293,7 +1678,7 @@ declare namespace App {
</TabPanel>
</Tabs>
### Check the user on the client
#### Check the user on the client
<Tabs
scrollable
@@ -1335,7 +1720,7 @@ declare namespace App {
</TabPanel>
</Tabs>
### withPageAuth
#### withPageAuth
<Tabs
scrollable
@@ -1411,7 +1796,7 @@ export const load: PageLoad = withAuth(async ({ session, getSupabaseClient }) =>
</TabPanel>
</Tabs>
### withApiAuth
#### withApiAuth
<Tabs
scrollable