SvelteKit + Supabase Security Blueprint

Share

To secure a SvelteKit + Supabase stack, you need to: (1) configure hooks.server.ts for Supabase client and session handling, (2) verify auth using getUser() in all +page.server.ts load functions, (3) verify auth in all form actions before mutations, (4) enable RLS as defense-in-depth, and (5) pass the Supabase client through locals. This blueprint covers SvelteKit's hooks system with Supabase SSR patterns.

TL;DR

SvelteKit's hooks system pairs perfectly with Supabase auth. Key tasks: use hooks.server.ts for session handling, verify auth in all +page.server.ts load functions and form actions, enable RLS as defense-in-depth, and use locals for passing the Supabase client.

Server Hooks Setup SvelteKit Supabase

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, path: '/' })
        },
        remove: (key, options) => {
          event.cookies.delete(key, { ...options, path: '/' })
        },
      },
    }
  )

  event.locals.getSession = async () => {
    const { data: { session } } = await event.locals.supabase.auth.getSession()
    return session
  }

  return resolve(event)
}

Protected Load Functions SvelteKit

src/routes/dashboard/+page.server.ts
import { redirect } from '@sveltejs/kit'

export async function load({ locals }) {
  const { data: { user } } = await locals.supabase.auth.getUser()

  if (!user) {
    throw redirect(303, '/login')
  }

  const { data: posts } = await locals.supabase
    .from('posts')
    .select('*')
    .eq('author_id', user.id)

  return { posts, user }
}

Secure Form Actions SvelteKit

Form actions with auth
export const actions = {
  create: async ({ locals, request }) => {
    const { data: { user } } = await locals.supabase.auth.getUser()

    if (!user) {
      return fail(401, { error: 'Unauthorized' })
    }

    const formData = await request.formData()

    await locals.supabase.from('posts').insert({
      title: formData.get('title'),
      author_id: user.id  // Use verified user ID
    })

    return { success: true }
  }
}

Security Checklist

Pre-Launch Checklist

RLS enabled on all tables

Auth verified in all load functions

Auth verified in all form actions

Hooks properly configured

Environment variables set

Alternative Stacks

Consider these related blueprints:

Building with this stack?

Scan for RLS and auth issues.

Start Free Scan
Security Blueprints

SvelteKit + Supabase Security Blueprint