First keep things as simple as can be:

pnpx sv create
# select Lucia as one dependency
# say yes when prompted to create a demo

Learn the fundamentals

In the protected page

How authentication will work: When I move to a page /demo/lucia, I will check in the server page load, whether the user is logged in. If so, I redirect him to /demo/lucia/login else I just return the user information that is stored in event.locals.user.

/demo/lucia/+page.server.ts
import type { PageServerLoad } from './$types';
 
export const load: PageServerLoad = async (event) => {
	if (!event.locals.user) {
		return redirect(302, '/demo/lucia/login');
	}
	return { user: event.locals.user };
};

In the login page

I will first check if the user is already logged in then I redirect him to the /demo/lucia page as he has no business to be in the login page.

/login/+page.server.ts
import type { PageServerLoad } from './$types';
 
export const load: PageServerLoad = async (event) => {
	if (event.locals.user) {
		return redirect(302, '/demo/lucia');
	}
	return {};
};

Now, I have a setup that redirects me (the user) to the right place based on whether I am authenticated or not.

If the user is not authenticated, I should give him a way to authenticate himself.

So, first I’ll create a server action. The way you do that in svelte is like so:

/login/+page.server.ts
import type { Actions, PageServerLoad } from './$types';
 
export const actions: Actions = {
	login: async (event) => {
		const formData = await event.request.formData();
		const username = formData.get('username');
		const password = formData.get('password');
		/*
		 * Logic for authentication
		*/
	}
}
 

Note that actions has to be exported and it contains many asynchronous functions with the names (here, login) that will be used to reference the API endpoint on the client. The way we get the formData is by awaiting the formData function from event.request.