diff --git a/apps/dev/sveltekit/src/components/header.svelte b/apps/dev/sveltekit/src/components/header.svelte index 2ad2d285b1..63e5ad1176 100644 --- a/apps/dev/sveltekit/src/components/header.svelte +++ b/apps/dev/sveltekit/src/components/header.svelte @@ -35,5 +35,7 @@ diff --git a/apps/dev/sveltekit/src/helpers.ts b/apps/dev/sveltekit/src/helpers.ts new file mode 100644 index 0000000000..37f205b3a2 --- /dev/null +++ b/apps/dev/sveltekit/src/helpers.ts @@ -0,0 +1,20 @@ +import type { RequestEvent } from "@sveltejs/kit" + +const PROTECTED_ROUTE_IDENTIFIER = "(protected)" +const ADMIN_ROUTE_IDENTIFIER = "(admin)" + +/** + * Checks if the route is protected. + * @param event - The request event. + * @returns True if the route is protected, false otherwise. + */ +export const isProtectedRoute = (event: RequestEvent): boolean => + event.route.id?.includes(PROTECTED_ROUTE_IDENTIFIER) ?? false + +/** + * Checks if the route is an admin route. + * @param event - The request event. + * @returns True if the route is an admin route, false otherwise. + */ +export const isAdminRoute = (event: RequestEvent): boolean => + event.route.id?.includes(ADMIN_ROUTE_IDENTIFIER) ?? false diff --git a/apps/dev/sveltekit/src/hooks.server.ts b/apps/dev/sveltekit/src/hooks.server.ts index 69f0f9ed9f..f88ee33e52 100644 --- a/apps/dev/sveltekit/src/hooks.server.ts +++ b/apps/dev/sveltekit/src/hooks.server.ts @@ -1 +1,36 @@ -export { handle } from "./auth" +import type { Session } from "@auth/sveltekit" +import { error, redirect, type Handle } from "@sveltejs/kit" +import { sequence } from "@sveltejs/kit/hooks" +import { handle as authenticationHandle } from "./auth" +import { isAdminRoute, isProtectedRoute } from "./helpers.js" + +// Handle this properly. This is just a placeholder. +const isAdmin = (session: Session | null) => + session && session.user?.email === "admin@example.com" + +const authorizationHandle: Handle = async ({ event, resolve }) => { + const session = await event.locals.auth() + + if (isProtectedRoute(event) && !session) { + // Redirect to the signin page + console.error("Unauthorized access to secure route") + throw redirect(303, "/auth/signin") + } + + if (isAdminRoute(event) && !isAdmin(session)) { + // Redirect to the home page + console.error("Unauthorized access to admin route") + throw error(403, "Forbidden") + } + + // If the request is still here, just proceed as normally + return resolve(event) +} + +// First handle authentication, then authorization +// Each function acts as a middleware, receiving the request handle +// And returning a handle which gets passed to the next function +export const handle: Handle = sequence( + authenticationHandle, + authorizationHandle, +) diff --git a/apps/dev/sveltekit/src/routes/(client-protected)/client-protected/+page.svelte b/apps/dev/sveltekit/src/routes/(client-protected)/client-protected/+page.svelte new file mode 100644 index 0000000000..aaccb956cc --- /dev/null +++ b/apps/dev/sveltekit/src/routes/(client-protected)/client-protected/+page.svelte @@ -0,0 +1,10 @@ + + +{#if $page.data.session} +

Protected without hooks.server.ts

+

This page is protected and can only be accessed by authenticated users.

+{:else} +

Access Denied

+{/if} diff --git a/apps/dev/sveltekit/src/routes/(protected)/(admin)/users/+page.server.ts b/apps/dev/sveltekit/src/routes/(protected)/(admin)/users/+page.server.ts new file mode 100644 index 0000000000..8c9501e3a8 --- /dev/null +++ b/apps/dev/sveltekit/src/routes/(protected)/(admin)/users/+page.server.ts @@ -0,0 +1,4 @@ +// hooks.server.ts won't be triggered without this +export const load = async () => { + return {} +} diff --git a/apps/dev/sveltekit/src/routes/(protected)/(admin)/users/+page.svelte b/apps/dev/sveltekit/src/routes/(protected)/(admin)/users/+page.svelte new file mode 100644 index 0000000000..683967f94e --- /dev/null +++ b/apps/dev/sveltekit/src/routes/(protected)/(admin)/users/+page.svelte @@ -0,0 +1,13 @@ + + +

Protected page - Admin

+

+ This is a protected content. You can access this content because you are + signed in and you are an admin. +

+

Session expiry: {$page.data.session?.expires}

+

User: {user?.name} ({user?.email})

diff --git a/apps/dev/sveltekit/src/routes/(protected)/protected/+page.server.ts b/apps/dev/sveltekit/src/routes/(protected)/protected/+page.server.ts new file mode 100644 index 0000000000..8c9501e3a8 --- /dev/null +++ b/apps/dev/sveltekit/src/routes/(protected)/protected/+page.server.ts @@ -0,0 +1,4 @@ +// hooks.server.ts won't be triggered without this +export const load = async () => { + return {} +} diff --git a/apps/dev/sveltekit/src/routes/(protected)/protected/+page.svelte b/apps/dev/sveltekit/src/routes/(protected)/protected/+page.svelte new file mode 100644 index 0000000000..2d399d44f5 --- /dev/null +++ b/apps/dev/sveltekit/src/routes/(protected)/protected/+page.svelte @@ -0,0 +1,13 @@ + + +

Protected page

+

+ This is a protected content. You can access this content because you are + signed in. +

+

Session expiry: {$page.data.session?.expires}

+

User: {user?.name} ({user?.email})

diff --git a/apps/dev/sveltekit/src/routes/protected/+page.svelte b/apps/dev/sveltekit/src/routes/protected/+page.svelte deleted file mode 100644 index 4acf5a3f92..0000000000 --- a/apps/dev/sveltekit/src/routes/protected/+page.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - -{#if $page.data.session} -

Protected page

-

- This is a protected content. You can access this content because you are - signed in. -

-

Session expiry: {$page.data.session?.expires}

-{:else} -

Access Denied

-{/if}