How to Manage Sessions in SvelteKit with SvelteKitAuth
Chapter 10 of Comprehensive Guide to SvelteKitAuth: Secure Authentication for SvelteKit Apps
We will explore two approaches to managing sessions in SvelteKit: one on the server side and the other on the client side.
Managing session on the Server side
As soon as the authentication is successful, SvelteKitAuth populates user sessions within locals that are accessible across the server-side code (i.e., hooks.server.ts, +page.server.ts, +layout.server.ts, +server.ts). Here is the programmatic representation of the statement:
src/hooks.server.ts
const { handle: getAuthConfig } = SvelteKitAuth(async (event) => {
const config: SvelteKitAuthConfig = {...}
});
export const handle = SvelteKitAuth(getAuthConfig) satisfies Handle;
// this adds { users, expires } property to locals
In server files, i.e., src/\.server.ts*,
We can access the session using the auth()
method. Here, the session
object will hold the user
property that contains the user information and the expires
property that depicts until which session is valid.
// [page | layout].server.ts
export const load = async (event) => {
const session = await event.locals.auth();
const isUserLoggedIn = !isEmpty(session?.user);
if (isUserLoggedIn) {
// execute business logic
} else {
// redirect user to sign-in oage
}
return { session };
};
// +server.ts
export const POST = (async ({ locals }) => {
const session = await locals.auth();
const isUserLoggedIn = !isEmpty(session?.user);
if (isUserLoggedIn) {
// execute business logic
} else {
// redirect user to sign-in oage
}
};
We can verify if the user is authenticated or not based on the user
and expires
properties.
NOTE: The expires
property updates itself every time the call to auth()
is made, thus extending the user session.
Lastly, the page and layout server files can return this session
object so that it is available on the client side as well.
Managing session on the Client side
The session management on the client starts from the session
property that was returned by parent layout or page server files. It will be available inside the $page
store, in the data
property: $page.data
. In this case, we return an object with the session
property, which is what we are accessing in the other code paths.
In the src/routes/+page.svelte.ts file, we can access the session variable:
<script>
import { page } from '$app/stores';
</script>
{#if $page.data?.session?.user}
<span>Display User specific Information</span>
{/if}
In universal load functions, src/routes/+[layout | page].ts, we can access the session variable:
export const load = (async ({ data }) => {
const session = data.session;
const isUserLoggedIn = !isEmpty(session?.user);
if (isUserLoggedIn) {
// execute business logic
} else {
// redirect user to sign-in oage
}
return { session };
});
This mechanism allows us to protect components from unauthorized access, i.e., Handling Authorization Per Component.
Coming to the second use case to protect routes/paths from unauthorized access, i.e., Handling Authorization Per Route. For this scenario, we first create an API route that returns the current session status.
src/routes/api/get-session-status/+server.ts
import type { RequestHandler } from "@sveltejs/kit";
import { isEmpty } from "lodash-es";
export const GET = (async (event) => {
let returnValue = { status: 200 };
try {
const session = await event.locals.auth();
const user = session?.user;
const isUserLoggedIn = !isEmpty(session) && !isEmpty(user);
returnValue = isUserLoggedIn ? { status: 200 } : { status: 401 };
} catch (ex: any) {
console.log(`Exception occured while quering user session: ${ex?.message}`);
returnValue = { status: 401 };
}
return new Response(JSON.stringify(returnValue), { status: 200 });
}) satisfies RequestHandler;
Before navigating to each route, we can check if session is defined or not
<script lang="ts">
import { beforeNavigate, goto } from '$app/navigation';
beforeNavigate(async ({ to, cancel }) => {
if (to?.url && to.url.pathname !== '/') {
// check user session on every navigation
const request = await fetch(`${window.location.origin}/api/get-session-status`);
const response = await request.json();
const publicURLs = ['/ssr-login', '/ssr-logout', '/public', '/'];
if (response.status !== 200 && !publicURLs.includes(window.location.pathname)) {
// user is not-logged in, redirect to sign-in screen
cancel();
goto(`/`);
}
}
return true;
});
</script>
Conclusion
In conclusion, managing sessions in SvelteKit with SvelteKitAuth can be effectively handled both on the server side and the client side. On the server side, sessions are managed using the auth()
method, which provides a session
object containing user information and session expiration details. This session object can be accessed across various server-side files and returned to the client side for further use. On the client side, the session data is available in the $page
store, allowing for component-level and route-level authorization checks. By leveraging these mechanisms, developers can ensure secure and efficient session management in their SvelteKit applications.
Here is the link to the GitHub repository with the codebase. In the next article, we will learn how to rotate the refresh token in SvelteKitAuth.