SvelteKit with Supabase Auth Helpers

Supabase has recently announced the revamped auth helpers for Supabase with SvelteKit support.

What are Auth Helpers?

According to Supabase one of the challenges has been creating a simple experience for server-side rendering (SSR) enviro…


This content originally appeared on DEV Community and was authored by kvetoslavnovak

Supabase has recently announced the revamped auth helpers for Supabase with SvelteKit support.

What are Auth Helpers?

According to Supabase one of the challenges has been creating a simple experience for server-side rendering (SSR) environments. Auth Helpers are a collection of framework-specific utilities for Supabase Auth. They allow you to implement secure applications with little effort. These libraries include functions for protecting API routes and pages in your applications.

How to use Supabase Auth Helpers in SvelteKit Projet?

TL;DR

Just install these two npm packages:

npm install @supabase/auth-helpers-sveltekit
npm install @supabase/auth-helpers-svelte

Detailed Tutorial

Set Up SvelteKit Project

We will try to have a minimal bare bone project. You are free to add any styling,type checking etc later. Supabase has quite nice examples using TypeScript available here.

Let name the project SvelteKitSupabaseAuthApp.
Just use the Skeleton project, no TypeScript, no ESLint, no Prettier, no Playwright.

npm create svelte@latest SvelteKitSupabaseAuthApp
cd SvelteKitSupabaseAuthApp
npm install

Install Supabase Auth Helpers for SvelteKit

npm install @supabase/auth-helpers-sveltekit
npm install @supabase/auth-helpers-svelte

Set Up Supabase Project

  • Login in Supabase and create a new project on the Supabase dashboard.
  • Go to Authentication > Settings section and change User Sessions - Site URL from http://localhost:3000 to http://localhost:3000/5173 and save. This is a localhost address where SvelteKit serves the project in developer mode. This change was introduced with Vite 3.
  • Go to Settings > API section and get the URL and anon key of your project.
  • Go back to your SvelteKitSupabaseAuthApp project and in its root create a new .env file. Replace VITE_SUPABASE_URL with the URL from previous step and VITE_SUPABASE_ANON_KEY with anon key from previous step:
# .env
# Update these with your Supabase details from your project settings > API
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key

Supabase Client Creation

In src folder create a new folder lib and in this lib folder create a new file sb.js, here we create supabaseClient using our credentials from .env end export the supabaseClient so we can use its auth methods in our application.

We will use login with email in this app. Supabase sends confirmation email automatically when a user signs ups.

// lib/sb,js
import { createSupabaseClient } from '@supabase/auth-helpers-sveltekit';

const { supabaseClient } = createSupabaseClient(
    import.meta.env.VITE_SUPABASE_URL,
    import.meta.env.VITE_SUPABASE_ANON_KEY
);

export { supabaseClient };

Hooks

In SvelteKit hook enables us to use two important functions, a handle function and a getSession function. The handle function runs every time the SvelteKit server receives a request. The getSession function takes the event object and returns a session object that is accessible on the client, so we can for example access data concerning user and cookies.

In src folder create a new file hooks.js

// src/hooks.js
import { handleAuth } from '@supabase/auth-helpers-sveltekit';
import { sequence } from '@sveltejs/kit/hooks';

export const handle = sequence(
    ...handleAuth({
        cookieOptions: { lifetime: 1 * 365 * 24 * 60 * 60 }
    })
);

export const getSession = async (event) => {
    const { user, accessToken } = event.locals;
    return {
        user,
        accessToken
    };
};

Layout

In src/routes/ folder create a new layout file __layout.svelte so we can not only have a simple layout but more importantly introduce our SupaAuthHelper component.

// src/routes/__layout.svelte
<script>
    import { session } from '$app/stores';
    import { supabaseClient } from '$lib/sb';
    import { SupaAuthHelper } from '@supabase/auth-helpers-svelte';
</script>

<svelte:head>
    <title>Email and Password Demo - Supabase Auth Helpers</title>
</svelte:head>

<SupaAuthHelper {supabaseClient} {session}>
    <main>
        <div>
            <h2>
                <a href="/">Supabase Auth Helpers Demo</a>
            </h2>

            <slot />

            <div>
                {#if $session?.user?.id}
                    <a href="/api/auth/logout">Sign out</a>
                {/if}
            </div>
        </div>
    </main>
</SupaAuthHelper>

Sign up Page and Route

In src/routes/ folder create a new file signup,svelte Just a simple sing up form.

// src/routes/signup,svelte
<script>
    export let errors = null;
    export let values = null;
    export let message = null;
</script>

<section>
    <div>
        <h1>Sign up</h1>
        {#if errors}
            <div>{errors.form}</div>
        {/if}
        {#if message}
            <div>{message}</div>
        {/if}
        <form method="post">
            <div>
                <label for="email">Email</label>
                <p>
                    <input
                        id="email"
                        name="email"
                        value={values?.email ?? ''}
                        type="email"
                        placeholder="Email"
                        required
                    />
                </p>
            </div>
            <div>
                <label for="password">Password</label>
                <p>
                    <input
                        id="password"
                        name="password"
                        value={values?.password ?? ''}
                        type="password"
                        placeholder="Password"
                        required
                    />
                </p>
            </div>
            <div>
                <p>
                    <button>Sign up</button>
                </p>
            </div>
        </form>

        <div>
            <p>
                Already have an account? <a href="/">Sign in</a>
            </p>
        </div>
    </div>
</section>

In src/routes/ folder create a new signup.js file. This endpoint points existing user to /dashboard which is protected user only page or provides sing up data to supabaseClient. We are also adding object specifying redirect when client clicks confirmation link in a confirmation email that Supabase will send him/her.

// src/routes/signup.js
import { supabaseClient } from '$lib/sb';

export const GET = async ({ locals }) => {
    // if the user is already logged in, then redirect to the dashboard
    if (locals.user) {
        return {
            status: 303,
            headers: {
                location: '/dashboard'
            }
        };
    }
    return {
        status: 200
    };
};

export const POST = async ({ request, url }) => {
    const data = await request.formData();

    const email = data.get('email');
    const password = data.get('password');

    const errors = {};
    const values = { email, password };

    const { error } = await supabaseClient.auth.signUp(
        { email, password }, 
        { redirectTo: `${url.origin}/logging-in`}
    );

    if (error) {
        errors.form = error.message;
        return {
            status: 400,
            body: {
                errors,
                values
            }
        };
    }

    return {
        status: 200,
        body: {
            message: 'Please check your email for a confirmation email.'
        }
    };
};

Log in (index) Page and Route

In src/routes/ folder replace index.svelte with this new one.Just a simple login form.

// src/routes/index.svelte
<script>
    export let errors;
    export let values;
</script>

<section>
    <div>
        <h1>Sign in</h1>
        {#if errors}
            <div>{errors.form}</div>
        {/if}
        <form method="post">
            <div>
                <label for="email">Email</label>
                <p>
                    <input
                        id="email"
                        name="email"
                        value={values?.email ?? ''}
                        type="email"
                        placeholder="Email"
                        required
                    />
                </p>
            </div>
            <div>
                <label for="password">Password</label>
                <p>
                    <input
                        id="password"
                        name="password"
                        value={values?.password ?? ''}
                        type="password"
                        placeholder="Password"
                        required
                    />
                </p>
            </div>
            <div>
                <p>
                    <button>Sign in</button>
                </p>
            </div>
        </form>

        <div>
            <p>
                Don't have an account? <a href="/signup">Sign up</a>
            </p>
        </div>
    </div>
</section>

In src/routes/ folder create a new index.js file. This endpoint points existing user to /dashboard which is protected user only page or provides login data to supabaseClient, returned session is use for a cookie creation.

//src/routes/index.js
import { supabaseClient } from '$lib/sb';

export const GET = async ({ locals }) => {
    if (locals.user) {
        return {
            status: 303,
            headers: {
                location: '/dashboard'
            }
        };
    }
    return {
        status: 200
    };
};

export const POST = async ({ request, url }) => {
    const data = await request.formData();

    const email = data.get('email');
    const password = data.get('password');

    const headers = { location: '/dashboard' };
    const errors = {};
    const values = { email, password };

    const { session, error } = await supabaseClient.auth.signIn({ email, password });

    if (error) {
        errors.form = error.message;
        return {
            status: 400,
            body: {
                errors,
                values
            }
        };
    }

    if (session) {
        const response = await fetch(`${url.origin}/api/auth/callback`, {
            method: 'POST',
            headers: new Headers({ 'Content-Type': 'application/json' }),
            credentials: 'same-origin',
            body: JSON.stringify({ event: 'SIGNED_IN', session })
        });

        // TODO: Add helper inside of auth-helpers-sveltekit library to manage this better
        const cookies = response.headers
            .get('set-cookie')
            .split('SameSite=Lax, ')
            .map((cookie) => {
                if (!cookie.includes('SameSite=Lax')) {
                    cookie += 'SameSite=Lax';
                }
                return cookie;
            });
        headers['Set-Cookie'] = cookies;
    }
    return {
        status: 303,
        headers
    };
};


Email confirmation redirect page

In src/routes/ folder create a new file logging-in@blank.svelte so we can check if the user redirected after email confirmation has been already set in session store. We will also provide a special layout for this case later on. During my tests the redirect and session store were quite fast so this page may be visible rather rarely.

src/routes/logging-in@blank.svelte
<script>
    import { session } from '$app/stores';
    import { goto } from '$app/navigation';
    import { page } from '$app/stores';

    let redirectPath = '/dashboard';

    $: {
        const redirectTo = $page.url.searchParams.get('redirect');
        if (redirectTo) {
            redirectPath = redirectTo;
        }
        // check if user has been set in session store then redirect
        if ($session?.user?.id) {
            goto(redirectPath);
        }
    }
</script>

<section>
    <div>
        <progress class="progress" max="100" />
    </div>
    <div>
        Signing in from the email confirmation link  ...
    </div>
</section>

<style>
    .progress:indeterminate {
        animation-duration: 3.8s;
    }
</style>

Dashboard Page and Route - protected users only page

In src/routes/ folder create a new file dashboard.svelte where we display some data about user stored by Supabase,

src/routes/dashboard.svelte 
<script>
    export let user;
</script>

<div>
    <h3>This is protected route accesible only for ligged users</h3>
    <p>Hello user {user.email}</p>
</div>
<div>
    <p>User {user.email} details:</p>
    <pre>{JSON.stringify(user, null, 2)}</pre>
</div>

In src/routes/ folder create a new dashboard.js file.

src/routes/dashboard.js
import { supabaseServerClient, withApiAuth } from '@supabase/auth-helpers-sveltekit';

export const GET = async ({ locals, request }) =>
    withApiAuth(
        {
            redirectTo: '/',
            user: locals.user
        },
        async () => {
            // const { data } = await supabaseServerClient(request).from('test').select('*');
            return {
                body: {
                    // data,
                    user: locals.user
                }
            };
        }
    );

Email Confirmation Redirect Layout

In src/routes/ folder create a new file __layout-blank.svelte where we are having a special named layout for email confirmation redirect as mentioned earlier.

//src/routes/__layout-blank.svelte
<script>
    import { session } from '$app/stores';
    import { supabaseClient } from '$lib/sb';
    import { SupaAuthHelper } from '@supabase/auth-helpers-svelte';
</script>

<svelte:head>
    <title>Email and Password Demo - Supabase Auth Helpers</title>
</svelte:head>

<SupaAuthHelper {supabaseClient} {session}>
    <slot />
</SupaAuthHelper>



I hope this tutorial was useful. Big thank to Supabase guys for SvelteKit Auth Helpers, their examples where true source for this article. Because I am really no auth expert especial y concerning SSR all comments, corrections or enhancements are welcomed.


This content originally appeared on DEV Community and was authored by kvetoslavnovak


Print Share Comment Cite Upload Translate Updates
APA

kvetoslavnovak | Sciencx (2022-07-24T20:25:00+00:00) SvelteKit with Supabase Auth Helpers. Retrieved from https://www.scien.cx/2022/07/24/sveltekit-with-supabase-auth-helpers/

MLA
" » SvelteKit with Supabase Auth Helpers." kvetoslavnovak | Sciencx - Sunday July 24, 2022, https://www.scien.cx/2022/07/24/sveltekit-with-supabase-auth-helpers/
HARVARD
kvetoslavnovak | Sciencx Sunday July 24, 2022 » SvelteKit with Supabase Auth Helpers., viewed ,<https://www.scien.cx/2022/07/24/sveltekit-with-supabase-auth-helpers/>
VANCOUVER
kvetoslavnovak | Sciencx - » SvelteKit with Supabase Auth Helpers. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/07/24/sveltekit-with-supabase-auth-helpers/
CHICAGO
" » SvelteKit with Supabase Auth Helpers." kvetoslavnovak | Sciencx - Accessed . https://www.scien.cx/2022/07/24/sveltekit-with-supabase-auth-helpers/
IEEE
" » SvelteKit with Supabase Auth Helpers." kvetoslavnovak | Sciencx [Online]. Available: https://www.scien.cx/2022/07/24/sveltekit-with-supabase-auth-helpers/. [Accessed: ]
rf:citation
» SvelteKit with Supabase Auth Helpers | kvetoslavnovak | Sciencx | https://www.scien.cx/2022/07/24/sveltekit-with-supabase-auth-helpers/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.