Supabase
Implementing Authentication with Supabase and SvelteKit
A complete guide to setting up user authentication in SvelteKit using Supabase Auth, including SSR support.
Implementing Authentication with Supabase and SvelteKit
Setting up authentication shouldn’t be painful. Supabase makes it remarkably simple while giving you full control over your data. Let’s build a complete auth flow.
Why Supabase for Auth?
- Multiple providers: Email, OAuth (Google, GitHub, etc.), Magic Links
- Row Level Security: Protect your data at the database level
- Built-in user management: No need to build admin panels
- SvelteKit integration: Works seamlessly with SSR
Project Setup
First, install the Supabase packages:
npm install @supabase/supabase-js @supabase/ssr Creating the Supabase Client
Set up both server and client-side Supabase instances:
// src/lib/supabase.ts
import { createBrowserClient } from '@supabase/ssr';
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public';
export const supabase = createBrowserClient(
PUBLIC_SUPABASE_URL,
PUBLIC_SUPABASE_ANON_KEY
); For server-side, we need a different approach:
// src/hooks.server.ts
import { createServerClient } from '@supabase/ssr';
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
event.locals.supabase = createServerClient(
PUBLIC_SUPABASE_URL,
PUBLIC_SUPABASE_ANON_KEY,
{
cookies: {
get: (key) => event.cookies.get(key),
set: (key, value, options) => {
event.cookies.set(key, value, options);
},
remove: (key, options) => {
event.cookies.delete(key, options);
}
}
}
);
event.locals.getSession = async () => {
const { data: { session } } = await event.locals.supabase.auth.getSession();
return session;
};
return resolve(event);
}; Building the Login Form
A simple login component:
<script lang="ts">
import { supabase } from '$lib/supabase';
let email = $state('');
let password = $state('');
let loading = $state(false);
let error = $state('');
async function handleLogin() {
loading = true;
error = '';
const { error: authError } = await supabase.auth.signInWithPassword({
email,
password
});
if (authError) {
error = authError.message;
}
loading = false;
}
</script>
<form onsubmit={handleLogin}>
<input type="email" bind:value={email} placeholder="Email" />
<input type="password" bind:value={password} placeholder="Password" />
{#if error}
<p class="error">{error}</p>
{/if}
<button type="submit" disabled={loading}>
{loading ? 'Signing in...' : 'Sign In'}
</button>
</form> Protecting Routes
Use SvelteKit’s load functions to check authentication:
// src/routes/dashboard/+page.server.ts
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ locals }) => {
const session = await locals.getSession();
if (!session) {
throw redirect(303, '/login');
}
return {
user: session.user
};
}; Row Level Security
Don’t forget to set up RLS policies in Supabase:
-- Enable RLS
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
-- Users can only see their own profile
CREATE POLICY "Users can view own profile"
ON profiles FOR SELECT
USING (auth.uid() = id);
-- Users can update their own profile
CREATE POLICY "Users can update own profile"
ON profiles FOR UPDATE
USING (auth.uid() = id); What’s Next?
This covers the basics! In upcoming posts, we’ll explore:
- Social OAuth providers
- Magic link authentication
- Role-based access control
- Real-time subscriptions with auth
Supabase continues to impress with how much it simplifies backend development!