Set up Auth using SvelteKit and Pocketbase
Do you want to learn how to set up authentication and authorization (auth) using SvelteKit and Pocketbase? In this guide, we will first set up a new SvelteKit project and then add them both to our new project. Besides this, we will also quickly discuss the difference between the two terms.
So let’s get started!
This is the second part of my free Pocketbase course, check the introduction and a list of all posts here.
- Set up a SvelteKit Project using Skeleton
- Set up the Pocketbase client
- Handle authentication and authorization (auth)
- Conclusion
Set up a new SvelteKit project using Skeleton
To set up a new SveleKit project with Skeleton UI installed, we can simply use the skeleton CLI. It sets up everything for us, and we just have to choose a few configurations that we want. We can run the setup using:
pnpm create skeleton-app@latest frontend
For this project, we choose:
- Bare Bones
- The Skeleton theme
- All packages, but CodeBlock
- And we use Typescript
- And then use whatever tools you want, I installed prettier
- Finally, press enter
After we set up the new project, we can enter the directory using cd frontend
. We then install the necessary node modules using pnpm install
and run the dev mode using pnpm dev
.
Need help or want to share feedback? Join my discord community!
With that, we have set up the SvelteKit project. In the next step, we will set up the Pocketbase client.
Set up the Pocketbase client
Before setting up the client, we need to install the Pocketbase SDK. We do so by running pnpm add pocketbase
.
If this guide is helpful to you and you like what I do, please support me with a coffee!
For this, we will create a new file called src/hooks.server.ts which includes a so-called handle function that is called every time a user sends a request to our site. Here we will also set up authentication and authorization (auth) later on.
After you create the file, open it and add the following content:
import PocketBase from 'pocketbase';
import { env } from '$env/dynamic/private'
import type { Handle } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks';
export const authentication: Handle = async ({ event, resolve }) => {
event.locals.pb = new PocketBase(env.PB_URL);
return await resolve(event);
}
export const handle = sequence(authentication)
In it, we first import the SDK and then create a function for authentication. In it, we create a new client by running Pocketbase with the URL of our pocketbase instance. To be able to easily change the URL later, we are using an environment variable. For the variable, we create a new .env
file in the frontend root with the line PB_URL=http://127.0.0.1:8090
.
Besides this, we have a handle function which creates a sequence with currently the authentication function and, later on also the authorization function.
As we also want to have types, we need to add the pocketbase type to the locals. For this, we open src/app.d.ts
and update it with the following content:
declare namespace App {
interface Locals {
pb: import('pocketbase').default;
}
// interface PageData {}
// interface Error {}
// interface Platform {}
}
Handle authentication and authorization (auth)
Before we start to set up authentication and authorization (auth) using SvelteKit and Pocketbase, let’s first discuss what it means:
- Authentication: is this a verified user?
- Authorization: is the authenticated user allowed to access the requested resource?
Now that we have that out of the way, let’s implement authentication and authorization using SvelteKit and Pocketbase. In the last step, we already created the authentication function, so let’s finish it up with the following code:
export const authentication: Handle = async ({ event, resolve }) => {
event.locals.pb = new PocketBase(env.PB_URL);
// load the store data from the request cookie string
event.locals.pb.authStore.loadFromCookie(event.request.headers.get('cookie') || '');
try {
// get an up-to-date auth store state by verifying and refreshing the loaded auth model (if any)
event.locals.pb.authStore.isValid && await event.locals.pb.collection('users').authRefresh();
} catch (_) {
// clear the auth store on failed refresh
event.locals.pb.authStore.clear();
}
const response = await resolve(event);
// send back the default 'pb_auth' cookie to the client with the latest store state
response.headers.append('set-cookie', event.locals.pb.authStore.exportToCookie());
return response;
}
In the function, we run a cookie-based authentication, which means that we check and set the cookies based on the requesting user. If the cookie is valid, we refresh it and send it back to the user. If it is not, we clear the auth store and thus do not verify the user.
Next up we handle authorization, so what the user is allowed to visit. For this, we create a second function called authorization and add it to the sequence. In it, we check which paths users are allowed to visit if authorized and, if they are not, where they should be redirected to:
const unprotectedPrefix = ['/login'];
export const authorization: Handle = async ({ event, resolve }) => {
// Protect any routes under /authenticated
if (!unprotectedPrefix.some((path) => event.url.pathname.startsWith(path)) && event.url.pathname !== '/') {
const loggedIn = await event.locals.pb.authStore;
if (!loggedIn) {
throw redirect(303, '/login');
}
}
// If the request is still here, just proceed as normally
const result = await resolve(event);
return result;
};
export const handle = sequence(authentication, authorization)
To determine the unprotected routes, I created an array called unprotectedPrefix
. In it, you can simply add all the paths that you want to be unprotected. Additionally, in the if clause, I also check for the path /
as if I would add it to the array, everything would be unprotected. It is added, for example to create a landing page of the project in the root. If this is not planned, you can also simply remove it.
Also, it is important to have the sequence be first authentication and then authorization, as we first need to check if it is a valid user, before checking if the user is allowed to access a certain resource.
In the next part of this course, we will create an email password login and registration to be able to actually log into our project. But for now, we have set up Pocketbase in our Sveltekit project, and we also equipped it with authentication and authorization, which is an important step.
You can also access the current state of the project in this repository and the branch called “course-2”.
Conclusion
In this part of the free PocketBase course, we learned how to set up a SvelteKit project using Skeleton UI and how to set up Pocketbase authentication and authorization (auth) inside of it. In the next part, we will use this foundation to set up new users using email and password registration. I hope you liked the course so far. See you in the next part!
In case you have any questions, feel free to ask them, and if you want to stay up to date with all my posts, consider subscribing to my monthly newsletter!
[convertkit form=2303042]