40
loading...
This website collects cookies to deliver better user experience
auth.onAuthStateChange()
callback:// src/routes/__layout.svelte
import { session } from '$app/stores';
import { supabase } from '$lib/supabaseClient';
import { setAuthCookie, unsetAuthCookie } from '$lib/utils/session';
// this should run on every page so I put this code in my `__layout.svelte` file
supabase.auth.onAuthStateChange(async (event, _session) => {
if (event !== 'SIGNED_OUT') {
session.set({ user: _session.user });
await setAuthCookie(_session);
} else {
session.set({ user: { guest: true } });
await unsetAuthCookie();
}
});
setAuthCookie
makes a request to an endpoint with the JWT and responds with a cookie and unsetAuthCookie
unsets the same cookie:// src/lib/utils/session.js
export async function setServerSession(event, session) {
await fetch('/api/auth.json', {
method: 'POST',
headers: new Headers({ 'Content-Type': 'application/json' }),
credentials: 'same-origin',
body: JSON.stringify({ event, session })
});
}
export const setAuthCookie = async (session) => await setServerSession('SIGNED_IN', session);
export const unsetAuthCookie = async () => await setServerSession('SIGNED_OUT', null);
supabase-js
library expects it.// src/routes/api/auth.json.js
export async function post(req /*, res: Response (read the notes below) */) {
// Unlike, Next.js API handlers you don't get the response object in a SvelteKit endpoint. As a result, you cannot invoke the below method to set cookies on the responses.
// await supabaseClient.auth.api.setAuthCookie(req, res);
// `supabaseClient.auth.api.setAuthCookie(req, res)` is dependent on both the request and the responses
// `req` used to perform few validations before setting the cookies
// `res` is used for setting the cookies
return {
status: 200,
body: null
};
}
hook
because we don't have access to the response object to pass to the supabase.auth.setAuthCookie
function.hooks.js
file looks like this:// src/hooks.js
export const handle = async ({ request, resolve }) => {
// Parses `req.headers.cookie` adding them as attribute `req.cookies, as `auth.api.getUserByCookie` expects parsed cookies on attribute `req.cookies`
const expressStyleRequest = toExpressRequest(request);
// We can then fetch the authenticated user using this cookie
const { user } = await auth.api.getUserByCookie(expressStyleRequest);
// Add the user and the token to our locals so they are available on all SSR pages
request.locals.token = expressStyleRequest.cookies['sb:token'] || undefined;
request.locals.user = user || { guest: true };
// If we have a token, set the supabase client to use it so we can make authorized requests as that user
if (request.locals.token) {
supabase.auth.setAuth(request.locals.token);
}
let response = await resolve(request);
// if auth request - set cookie in response headers
if (request.method == 'POST' && request.path === '/api/auth.json') {
auth.api.setAuthCookie(request, toExpressResponse(response));
response = toSvelteKitResponse(response);
}
return response;
};
toExpressRequest
, toExpressResponse
, toSvelteKitResponse
look like this:// src/lib/utils/expressify.js
import * as cookie from 'cookie';
/**
* Converts a SvelteKit request to a Express compatible request.
* Supabase expects the cookies to be parsed.
* @param {SvelteKit.Request} req
* @returns Express.Request
*/
export function toExpressRequest(req) {
return {
...req,
cookies: cookie.parse(req.headers.cookie || '')
};
}
/**
* Converts a SvelteKit response into an Express compatible response.
* @param {SvelteKit.Response} resp
* @returns Express.Response
*/
export function toExpressResponse(resp) {
return {
...resp,
getHeader: (header) => resp.headers[header.toLowerCase()],
setHeader: (header, value) => (resp.headers[header.toLowerCase()] = value),
status: (_) => ({ json: (_) => {} })
};
}
/**
* Converts an Express style response to a SvelteKit compatible response
* @param {Express.Response} resp
* @returns SvelteKit.Response
*/
export function toSvelteKitResponse(resp) {
const { getHeader, setHeader, ...returnAbleResp } = resp;
return returnAbleResp;
}
localstorage
which makes it vulnerable to XSS. This is done so the supabase-js
client library can refresh the token on the users behalf.