- TypeScript 79.2%
- Svelte 13.9%
- JavaScript 4.8%
- HTML 2.1%
| assets | ||
| e2e | ||
| src | ||
| static | ||
| .env.example | ||
| .gitignore | ||
| .npmrc | ||
| .prettierignore | ||
| .prettierrc | ||
| drizzle.config.ts | ||
| local.db.bak | ||
| package-lock.json | ||
| package.json | ||
| playwright.config.ts | ||
| README.md | ||
| svelte.config.js | ||
| tsconfig.json | ||
| vite.config.ts | ||
| vitest-setup-client.ts | ||
SvelteKit Access Control Introduction
Enda Lee 2025
Introduction
Setting up using the built in Lucia Auth access control
Creating a new application
1. Setup a Sveltekit app in webapps/lab-6a
Enter the following command to start the setup process. . refers to the current directory.
npx sv create .
As before you will be prompted to selection options for the new application.
This is the same as in lab 1 except for the options where you should include drizzle and lucia
Also make sure to include the lucia demo.
2. Trying the access control demo
1. Start the application as usual, you should see a prompt to update the database schema.
This will add two tables, user and session to the SQLite database.
2. Proceed and start the application
3. Open http://localhost:5173/demo in your browser
You will see the very basic demo page with a link to the login form - open it:
4. Register an account (don't forget the password)
After registration you should see a confirmation. In addition to the message you should see that a cookie was set in the browser.
That's the demo, behind the scences is more interesting...
3. How it works
Although this is a very simple example, the authentication process is the same as for a more complex application.
1. Request for page intercepted by hooks.server.ts (see comments)
This check happens on every request to the application so that checking occurs before a route/ page is loaded.
// /hooks.server.ts
import type { Handle } from '@sveltejs/kit';
// import lucia auth functions
import * as auth from '$lib/server/auth';
const handleAuth: Handle = async ({ event, resolve }) => {
// 1. Try to get the session cookie from the browser request
const sessionToken = event.cookies.get(auth.sessionCookieName);
// 2. if no session cookie, clear the user and sesssion objects
if (!sessionToken) {
event.locals.user = null;
event.locals.session = null;
return resolve(event);
}
// 3. validate the session and retrieve the (logeed in) user
const { session, user } = await auth.validateSessionToken(sessionToken);
// 4. Set or delete the cookie
if (session) {
auth.setSessionTokenCookie(event, sessionToken, session.expiresAt);
} else {
auth.deleteSessionTokenCookie(event);
}
// 5. store user and session in state
event.locals.user = user;
event.locals.session = session;
return resolve(event);
};
export const handle: Handle = handleAuth;
2. continue to load routes/demo/lucia
+page.server.ts will load first on server-side - read coments for details
// routes/demo/lucia/+page.server.ts
import * as auth from '$lib/server/auth';
import { fail, redirect } from '@sveltejs/kit';
import { getRequestEvent } from '$app/server';
import type { Actions, PageServerLoad } from './$types';
// Login required for this route
// Check if user in locals object and return (to +page.svelte for this route)
export const load: PageServerLoad = async () => {
const user = requireLogin();
return { user };
};
// Form action to handle a request for logout
export const actions: Actions = {
logout: async (event) => {
// check if session exists
if (!event.locals.session) {
return fail(401);
}
// clear the session
await auth.invalidateSession(event.locals.session.id);
auth.deleteSessionTokenCookie(event);
// redirect to the login page
return redirect(302, '/demo/lucia/login');
}
};
// This function gets locals object if set (by server hooks)
function requireLogin() {
const { locals } = getRequestEvent();
// if no user redirect to login page
if (!locals.user) {
return redirect(302, '/demo/lucia/login');
}
// return user
return locals.user;
}
+page.svelte loaded - read coments for details
<script lang='ts'>
// Use Svelte form enhacements for submission
import { enhance } from '$app/forms';
import type { PageServerData } from './$types';
// Get the server data from the page load function
let { data }: { data: PageServerData } = $props();
</script>
<!-- Display user details about the logged in user-->
<h1>Hi, {data.user.username}!</h1>
<p>Your user ID is {data.user.id}.</p>
<!-- The logout form - sends a post request handled by the logout action in +page.server.ts-->
<form method='post' action='?/logout' use:enhance>
<button>Sign out</button>
</form>
4. The auth functions - src/lib/server/auth.ts
As part of lucia auth, auth.ts was added to the app. This script contains 'boilerplate' functions to handle session creation, validation, crypto, etc. The function names are self explanatary.
Exercises
Part 1
- Add some routes/ pages to app:
- Some of these will be public, e.g about, contact, login, register.
- Other will require login, e.g. user-profile
- Add a nav bar for your new pages in
+layout.svelteand use Bootstrap styling. - Following the demo example, and authentication checking on the routes which require login.
- Use Bootstrap to improve the login/ registration UI.
Part 2
Require authentication to access the /location route in Lab 6
- Use code from part 1 in your solution.
- The database already includes the required
userandsessiontables with 3 sample users:alice,bob, andtestall of which use the passwordPassword - Add a login link to the nav menu which should change to show logout after a successful login (see: https://svelte.dev/tutorial/svelte/else-if-blocks)
Enda Lee 2025





