How to Protect Your SvelteKit Routes
Many applications need to protect their routes for pages such as a user account page.
This blog will demonstrate how to protect SvelteKit routes with two different examples.
You can view the actual code for these examples on GitHub below.
Where we authenticate the user in?
Auth cookies can be checked inside server hooks. If a user is found matching the provided credentials, the user information can be stored in
locals
.
According to the SvelteKit official site, we will implement authentication in src/hooks.server.ts
.
These examples will NOT work in SPA mode because server hooks are only called when the SvelteKit server receives a request.
Folder structure
src
├── routes
│ ├── (protected)
│ │ ├── user
│ │ ├── +page.svelte
│ │ ├── +page.server.ts
│ ├── about
│ │ ├── +page.svelte
│ │ ├── +page.server.ts
│ ├── login
│ │ ├── +page.svelte
│ │ ├── +page.server.ts
│ ├── +page.svelte
│ ├── +page.server.ts
│ ├── +layout.svelte
│ ├── +layout.server.ts
├── hooks.server.ts
I will protect routes within the (protected)
folder.
Create empty +page.server.ts
files in each route to ensure hooks.server.ts
is called; otherwise, it will not be triggered.
I will not describe the details of each file, as they simply display basic text.
For now, let's focus on src/hooks.server.ts
.
Two ways to protect routes
- Directory name base protection (Recommended)
- Pathname base protection
Directory name base protection (Recommended)
import { redirect, type Handle } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks';
const authHandle: Handle = async ({ event, resolve }) => {
const response = await resolve(event);
// Get the root directory name
const rootDirName = event.route.id?.split('/')[1] || '';
const protectedDirName = '(protected)';
// Get the auth token from the cookie
const auth_token = event.cookies.get('auth_token');
// You may need authorize auth_token in real world.
// Redirect to the root if the user is not authenticated
if (
rootDirName === protectedDirName
&& !auth_token
) {
redirect(301, '/login');
}
return response;
};
export const handle = sequence(authHandle);
This approach allows you to protect routes within the (protected)
folder without any additional configuration. If you want to secure other routes, simply add files to this directory.
The event.route.id
property returns the ID of the current route. For example, for src/routes/(protected)/user
, it would return /(protected)/user
. You can check whether the current route is protected, and if so, validate the auth_token
. If the user is not authorized, redirect them to the login page.
In my opinion, this method is an excellent choice for starting small, as it is both intuitive and easy to maintain.
You can find more detailed code here.
Pathname base protection
import { redirect, type Handle } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks';
// Protected path list
const protectedPaths = ['/user'];
const authHandle: Handle = async ({ event, resolve }) => {
const response = await resolve(event);
// Get the path name
const pathName = event.url.pathname;
// Get the auth token from the cookie
const auth_token = event.cookies.get('auth_token');
// You may need authorize auth_token in real world.
// Redirect to the root if the user is not authenticated
// and check if the path is in the protected path list
if (
protectedPaths.some((protectedPath) => pathName.startsWith(protectedPath))
&& !auth_token
) {
redirect(301, '/login');
}
return response;
};
export const handle = sequence(authHandle);
You can protect routes defined in protectedPaths
, which provides greater flexibility by allowing path-level protection. However, you need to update protectedPaths
whenever new protected routes are added. This could become difficult to maintain as the project grows.
Additionally, the use of protectedPaths.some((protectedPath) => pathName.startsWith(protectedPath))
has a time complexity of O(n). As the number of routes in protectedPaths
increases, the time complexity will grow accordingly.
You can view the detailed code here.
Conclusion
Based on my experience, I recommend using directory name-based protection.
When I previously relied on pathname-based protection, it was challenging to develop applications. I often forgot to update protectedPaths
, and the project folder structure quickly became messy.
Using directory name-based protection offers better maintainability and is more intuitive, making it easier to manage in the long run.