SvelteKit and Authentication—server- or client-side?
Struggling to know “the best way” of doing
auth
in a SvelteKit app. With Supabase, it appears you can run supabaseClient.auth.signIn({ email: form.email, password: form.password });
server- or client-side:
1. Send username
and password
to a server-side route (/api/login
) which then runs supabaseClient.auth.signIn()
server-side, gets back a user and session, registers session and returns data to client for Supabase to know who's logged in. (This is how I did my first SvelteKit + Supabase app according to https://www.youtube.com/watch?v=znZE6DEtVNs)
2. Do auth in the client, then send session object back to the server so that SvelteKit's backend can register session
and then knows that the user is logged-in (or not), which helps with SSR protected routes (if not logged in, redirect to /login
). This appears to be how most of the tutorials now are structured, but it feels kinda weird... Like, I feel the server should be the source of truth, not the web browser.
Does this make any sense? Any thoughts or opinions?Svelte Mastery
YouTube
Supabase & Sveltekit - Auth w/ Cookies 🍪 (Part 3.5)
So now we get into how to store the session with cookies which lets us have a much better redirect experience for users!
Timestamps
0:00 - Intro
0:55 - App Overview
2:20 - Signup Code
4:04 - Set Cookie Code
5:58 - Get Cookie Code
7:17 - Redirect Code
8:37 - Logout Code
10:47 - Summary
CODE
https://github.com/sveltemaster/supasveltekit-cookies...
67 Replies
SvelteKit just removed the session store actually. This is the proposal that lead up to it and it looks really nice imo. https://github.com/sveltejs/kit/discussions/5883
I just migrated my routes stuff yesterday and would like to rewrite the auth stuff today. Will try to post an update here is it turns out nice.
GitHub
Removing
session
· Discussion #5883 · sveltejs/kitUpdate: session was removed in #5946 One of the great joys of designing APIs is the realisation, when you finally hit upon the right design, that previously hard problems become trivial to solve. A...
Following.
I've been hacking around with Supabase Auth and SvelteKit for a few days now. And so far my understanding is:
If you want to do auth or any sensitive user permissions related logic on the server side, there is this auth helper package from Supabase team, but I don't like that I have to wrap every call to Supabase into something like `withApi( ...select... ) and in general try to avoid using too many packages.
If you can implement all your data security (i.e. RBAC) with RLS policies, you can just use the Auth on client-side. This is what I currently go with (also because I decided to use Astro instead of SvelteKit for our project). And if I can't do something with RLS, well, not sure what I will do.
I have RLS but I still have doing most of my supabase calls server side. Much easier to get good SEO this way. I saw the
withApi
wrapper in the auth helper docs as well but I'm not using it at all. I'm passing the accessToken
directly to the supabase related functions and you can use it directly with await supabaseServerClient(accessToken).from(...
without the withApi
wrapper. Anyhow.. the auth helper does not work with the latest changes as mentioned above (see https://github.com/supabase/auth-helpers/issues/230 for more details) so I'm currently reverting to not use the auth helper library and incorporating the new changes without session.
Interesting that you decided to use Astro! I'm thinking about switching too but I would like to give the new svelte kit changes a more solid try first.Just saw that Rich posted a solution/workaround to make the auth helper (https://github.com/supabase/auth-helpers/tree/main/packages/sveltekit) work: https://github.com/sveltejs/kit/discussions/5883#discussioncomment-3430427
I already rewrote everything by myself without the auth helper for now but it's not clean atm so I won't share too much about that for now. Interested to see how the auth helper evolves after this flurry of changes for svelte kit.
GitHub
auth-helpers/packages/sveltekit at main · supabase/auth-helpers
A collection of framework specific Auth utilities for working with Supabase. - auth-helpers/packages/sveltekit at main · supabase/auth-helpers
GitHub
Removing
session
· Discussion #5883 · sveltejs/kitUpdate: session was removed in #5946 One of the great joys of designing APIs is the realisation, when you finally hit upon the right design, that previously hard problems become trivial to solve. A...
Yeah I’m still trying to figure out how he means to use this
Do you still use the auth helper code in the hooks.ts?
Would be nice to get away from using the Auth Helper altogether and just write the session cookie in an endpoint. Then the load function can provide the user to any page wanting it, no need for the Front End provider at all then
I have personally removed the auth helper for now because I'm impatient. The new changes with
+layout.server.ts
makes it really straightforward to implement. The bonus super nice thing is that after these changes, I no longer need the hooks.ts
and app.d.ts
files. The following is the gist of what I did.
1. Add a route at src/routes/api/auth/+server.ts
, in which I have a POST
function defined. Here I read the accessToken
and refreshToken
, which are sent from the function in the next step. If the tokens are provided, we set the cookie headers that are then sent back to the client and added to the browser.
2. Whenever the supabase session data changes (eg. after sign-in, sign-out, or refresh etc), I post the access and refresh tokens to the route from step 1. The function looks like the following (the type Session
here is from import type { Session } from '@supabase/gotrue-js';
).
Let me know if this doesn't make sense and I would be happy to provide more details.Thanks so much for this, just trying to get my head around it all. So if you’ve removed auth-helper stuff, how do you call Supabase with the token from the server side?
Rich mentions being able to use the
handle
method to write to the locals variable … is this easier? I’m just not getting how this hangs together. What happens when my magic link is resolved to the redirectTo
path? How do I get the session from that and set it in the app so I can access it from the locals
variable in all my server side code? https://github.com/sveltejs/kit/discussions/5883#discussioncomment-3393658GitHub
Removing
session
· Discussion #5883 · sveltejs/kitUpdate: session was removed in #5946 One of the great joys of designing APIs is the realisation, when you finally hit upon the right design, that previously hard problems become trivial to solve. A...
Once the cookies get added to the browser following the previous steps, the cookies will be available on the subsequent requests and I have a helper function like this that I use in
+layout.server.ts
.
Then in the same layout file you want to include the accessToken
along with the user
as similar to how Rich described in: https://github.com/sveltejs/kit/discussions/5883#discussion-4302953.
Now in the +page.server.ts
files, you can get the accessToken using: const { accessToken } = await parent();
. From here you can use the setAuth
function (https://supabase.com/docs/reference/javascript/auth-setauth) from supabase-js
with the accessToken
. Be careful here because as mentioned in the docs: "The JWT will then be sent in all subsequent network requests.". So you want to make sure that you are creating a new instance using createClient
each time.setAuth() | Supabase
Overrides the JWT on the current client. The JWT will then be sent in all subsequent network requests.
GitHub
Removing
session
· Discussion #5883 · sveltejs/kitUpdate: session was removed in #5946 One of the great joys of designing APIs is the realisation, when you finally hit upon the right design, that previously hard problems become trivial to solve. A...
I have not explored this alternative. I imagine the only difference from the method I described above is that you don't have to do
const { accessToken } = await parent();
. I prefer being explicit about it though and opt-in for the routes I need instead.Thanks so much … I’d actually ended up with the same solution as you (minus the user data) last night, but I can’t figure out how to handle expired tokens and force the refresh.
Totally baffled by all of this ... is there a step by step about how this supabase auth works?
When you click the link in the magic link email it hits an endpoint in supabase, what does it do next?
I'm guessing the
redirectTo
is where it goes AFTER it's been verified, but how does it do that?
Is there a specific endpoint on the client app that it's expecting to hit which then extracts the data from the URL and stores it as cookies.
I understand how once we've got cookies to pass them around the app, but how do you get to that stage?
Also how do you deal with expired tokens etc.
Or are you still using AuthHelpers for some stuff?Using magiclink comes back to your app at the redirectTo address. The supabase client code will look at the url for a token and then onAuthStateChanged will be fired with a SIGNED_IN event. It also sets up the session and stores that to localstorage.
...
let me clarify my confusion 🤣
I understand everything you explained about getting the cookies from the req.header and passing them around and using them, I just don't understand how we get those cookies, how we create them.
You mentioned that you post them to
setServerSession
but where do you get session
from e.g. in the login function ... and how does the POST
function in api/auth/+server.ts
set the cookies in browser storage?
I think that's all I'm missing.
I think the access token comes back as a hash parameter in the URL, but I can't find info on that. Apologies for the stupidity here, just not used to dealing with low-level auth stuff.
Hi Gary, sure ... but we're trying to solve this the SSR way ... using the endpoints, not using the JS onAuthStateChanged handler which subscribes to events. We should be able to pull the data out of the URL ourselves, store the cookies and use them in the server endpoints, without needing to use any async stuff.I haven't found a solution that works with the managed service. The only way I've gotten full server-side auth is with changes to gotrue and self hosting.
https://discord.com/channels/839993398554656828/1007998446315241617/1008076722299736094 implies you have to have browser code for magic link.
But I've not read thru the entire thread here, (don't use server side stuff) so certainly don't have the full context of the thread.
what's in your
api/auth/+server.ts
file to save the cookies to the browser?
Just cannot figure it out. Spent hours on this now 😬
I've got it getting the tokens, calling the endpoint. I've got it reading the tokens stored in the cookies and using them.
I just can't update the cookies with the ones that have just been served.
I'm using your setServerSession
approach and calling a POST metho in /api/auth/+server.ts
... tried doing
it sets a new cookie alongside the old one, with the same name sb-access-token
but it doesn't get read when I get('cookie') in +layout.server.ts
load methodHi! Just got off work and catching up on this thread. This is what my
/routes/api/auth/+server.ts
looks like. Sorry it's not very DRY but it's easy for me to read.
Perfect, works a treat. Gonna try and simplify it and write it up. Getting weird type problems with RequestEvent and parent, need to figure that out.
I've used the
src/hooks.ts
file to set the locals variable instead of setting the response on the +layout.server.ts
and then having to use const {accessToken} = await parent()
in each of the load handlers ... I can just get it from locals.accessToken
I'm still getting weird Typescript errors with RequestEvent
... I also tried importing it from './$types'
I just get a different TS errorI use TS too but I didn't get any lint warnings about not specifying the types in this file. Try to take it (type imports) out and see what happens? There is a lot of magic type goodies with these new svelte kit changes.
Other than that I have not been able to keep up with all the new changes. Maybe it's best to just lay low for a couple of weeks. I'm checking out astro in the mean time because I just don't like the routing change esthetics. Might stick with svelte kit anyways for the type magic though.
Okay finally got there with all this ... I wrote it up as I went, hopefully someone else can benefit
Stu's Notion on Notion
Sveltekit + Supabase Magic Link Auth using SSR
Sveltekit encourages a progressive enhancement approach to webapp development by leveraging the power of SSR (Server-side rendering). Supabase is an excellent platform that provides storage, realtime DB, DB, edge functions and Auth out of the box and with minimal integration. Implementing Auth correctly is a non-trivial problem.
Thanks for sharing. I browsed through it a bit. In the code block of the "Making authenticated Supabase calls from endpoints" section, how is it making an authenticated call based on the current user?
Same here. SSR is just a nicer user experience, but it isn't easy or obvious (to me). Working on it...
Yeah, I don't like their auth helpers either. They seem (sorry to be blunt)... ugly.
the
hooks.ts
file handles auth changes and sets the supabase auth context supabase.auth.setAuth(accessToken);
... so any subsequent calls should use that (unless I've misunderstood how this hangs together and I should be getting the accessToken before every API call and calling setAuth? )Ah I see. Sorry I missed that bit. That sounds like it makes sense. I do vaguely remember the sessions getting leaked during testing at some point for my app though but not sure if it was because of this. Guess you can always test a bit with RLS and different users.
Does https://github.com/supabase/auth-helpers/tree/main/packages/sveltekit works with latest sveltekit?
GitHub
auth-helpers/packages/sveltekit at main · supabase/auth-helpers
A collection of framework specific Auth utilities for working with Supabase. - auth-helpers/packages/sveltekit at main · supabase/auth-helpers
No. Because Rich & Co. removed
session
. I'm trying to figure out how to make my app work again. Grr.Another thing regarding the code under "Making authenticated Supabase calls from endpoints":
can this block:
be abstracted away? Where would you move it? It looks like this is needed every time you call Supabase to select/insert etc....?
@Waldemar I don't know. The “simple” thing I want to do is redirect users from
/profile
to /login
if a session is not set. Maybe we should code-pair.Read here for exactly how to modify your auth helper code to work with the latest SK.
https://github.com/supabase/auth-helpers/issues/230
GitHub
SvelteKit removed Session store · Issue #230 · supabase/auth-helpers
Bug report Describe the bug The latest version of SvelteKit removes the Session store, breaking the Svelte and SvelteKit auth helpers. See: sveltejs/kit#5883 (comment) Expected behavior Be able to ...
Will look another look. I want to do server-side auth for SSR...
i'm deep stuck on this
does it works? I'm trying to follow throw but not working for me
I pressed
pause
on this last week until more of Internet figures things out. What I did was watch https://www.youtube.com/watch?v=LMTfzyVJIXs (excellent) then started a new SvelteKit project and figure out how cookies
are persisted in hooks.js
.James Q Quick
YouTube
SvelteKit Breaking Changes 2022 - My Reactions and What You Need to...
SvelteKit just announced some major breaking changes. Here's what you need to know!
Migrating Breaking Changes in SvelteKit - https://www.netlify.com/blog/migrating-breaking-changes-in-sveltekit/
What's New in Svelte - https://svelte.dev/blog/whats-new-in-svelte-august-2022
Routing Docs - https://kit.svelte.dev/docs/routing#page-page-js
Github ...
I'm following this guiide from @stukennedy https://stukennedy.notion.site/Sveltekit-Supabase-Magic-Link-Auth-using-SSR-08a94511da3e43c9b49ed6ce1f292e38, i get logged in but not cookies get saved and just get redirected back to the auth page
i'm checking supabase logs to check if the login when through, there is no error message
this code never gets executed

hey guys sorry
been deep in other stuff. Yeah I got it working. Lemme go back to my notion and update it
Stu's Notion on Notion
Sveltekit + Supabase Magic Link Auth using SSR
Sveltekit encourages a progressive enhancement approach to webapp development by leveraging the power of SSR (Server-side rendering). Supabase is an excellent platform that provides storage, realtime DB, DB, edge functions and Auth out of the box and with minimal integration. Implementing Auth correctly is a non-trivial problem.
okay try that now
if it doesn't work, please let me know
I've put it on a webpage
Stu's Notion on Notion
Sveltekit + Supabase Magic Link Auth using SSR
Sveltekit encourages a progressive enhancement approach to webapp development by leveraging the power of SSR (Server-side rendering). Supabase is an excellent platform that provides storage, realtime DB, DB, edge functions and Auth out of the box and with minimal integration. Implementing Auth correctly is a non-trivial problem.
Hi @stukennedy, do you have a variation that is not MagicLink, but regular username + password
auth()
? I will look at your example, maybe there's enough logic in common. Thanks for sharing!that should be a LOT simpler
actually maybe not, you may still have to jump through all the hoops to get the cookies set etc
Yes, my issue up until now is setting the cookie server-side and redirecting to
/login
when there is no session. I'll read your code and see what happens.I'd imagine you just copy my code
then add a password field
I'd like to avoid Supabase's auth helpers. The context thing is ugly.
Right.
yeah I think it'll be exactly the same as my code
only add a password input to the login page
you'd get the password value through the formData the same as I did for email
and then just call
that's a guess
(not tested)
obviously though with password you need the whole signup and forgot password journeys
I love magic link
Yeah I'm re-thinking password, maybe just MagicLink is better.
@stukennedy Read through, looks really good. Excited to try it. Thanks for the post.
no problem ... do let me know if you get stuck
i got it working with magic link but nos password, i'll try again, i need passwords because magic link is not feasible for my client/project
but thanks a lot for helping out
the redirectTo is being ignore by supabase so the its always redirecting to root /
never mind, it for some reason its working now 🙃
Why is it being ignored?
RedirectTo only works if you have the same url in the list of Redirect URLs in the Supabase Authentication settings on their dashboard
http://localhost:3000/logging-in
(I force VITE to make it port 3000 otherwise this messed up)
Hmm, I applied @stukennedy Notion article code, but get the following error on
/login
:
hooks.ts
looks like
Seems like if there is no cookie for sb-access-token
it fails. Should that not perhaps be wrapped in an if
?Yes … Possibly a ternary to set the user to null instead of trying to use it
Still stuck... Here's my horrible repo. https://bitbucket.org/r2-creative/gameonornot/src/main/
It looks like you are not using the password when you are calling the
signIn
function. https://bitbucket.org/r2-creative/gameonornot/src/04d633724ba4176b1e105bcb13e0ab3783d7a3ed/app/src/routes/login/%2Bpage.server.ts#lines-18:21
FWIW, I do this a bit differently. Once a user is signed in, I make all of the supabase calls server-side. But for the actual signing in/out/up etc, I do that directly on the frontend.Did you end up testing this more? The problem I'm wondering about is like this: say that you have two users A and B and for each of them you want to make two database calls, for (bad) example you have a server route that fetches a user's tweets, and then fetch a user's photos.
If user A makes the call first and then user B makes the same call immediately after, does that make it so that when user A is fetching the
photos
, supabase is fetching user B's photos
instead?I did test this, and it seemed to be behaving correctly
used 2 browsers and logged in to 2 accounts, and did a select on projects, it only recovered the projects for the user
That use case sounds like it should be safe since the race condition won’t really be reflected. Did you try to use setTimeout on the first user’s projects fetch to make sure that the call happens after the second user’s session got set?
i have it working as magic link but i cant access or save locals

this is my hook
fixed it doing this


for the types and passing the events to the page throw the load function
Hmm, even with your suggestions (thank you!), I'm still getting a
coalesce_to_error
error... from most recent commit https://bitbucket.org/r2-creative/gameonornot/src/main/app/src/.
It's got to be something obvious. Wish I could see it! 😆
Btw, once I/we get this working, I intend to make a sort of public template repo for a bare bones “SvelteKit + Supabase basic auth with email, password and/or magic link“ so others can get up and running without the pain.
just looking at the code again, I think my setting Auth in hooks/handle should work
handle gets called every time the sveltekit server receives a request (e.g. all my server methods post/get/put/delete/update) ... and it sets the auth at that point.
That's just the same as calling
setAuth
in the method instance right? (without the boilerplate)for some reason my code stop working and can't figure out why
😵💫
Pushed a commit to my work-in-progress side app that has auth working: https://bitbucket.org/r2-creative/gameonornot/commits/