Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve sveltekit example app with protected and admin routes #12283

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/dev/sveltekit/src/components/header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,7 @@
<div class="links">
<a class="linkItem" href="/">Home</a>
<a class="linkItem" href="/protected">Protected</a>
<a class="linkItem" href="/client-protected">Client Protected</a>
<a class="linkItem" href="/users">Admin</a>
</div>
</header>
20 changes: 20 additions & 0 deletions apps/dev/sveltekit/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -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
37 changes: 36 additions & 1 deletion apps/dev/sveltekit/src/hooks.server.ts
Original file line number Diff line number Diff line change
@@ -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 === "[email protected]"

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,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script lang="ts">
import { page } from "$app/stores"
</script>

{#if $page.data.session}
<h1>Protected without hooks.server.ts</h1>
<p>This page is protected and can only be accessed by authenticated users.</p>
{:else}
<h1>Access Denied</h1>
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// hooks.server.ts won't be triggered without this
export const load = async () => {
return {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script lang="ts">
import { page } from "$app/stores"

$: user = $page.data.session?.user
</script>

<h1>Protected page - Admin</h1>
<p>
This is a protected content. You can access this content because you are
signed in and you are an admin.
</p>
<p>Session expiry: {$page.data.session?.expires}</p>
<p>User: {user?.name} ({user?.email})</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// hooks.server.ts won't be triggered without this
export const load = async () => {
return {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script lang="ts">
import { page } from "$app/stores"

$: user = $page.data.session?.user
</script>

<h1>Protected page</h1>
<p>
This is a protected content. You can access this content because you are
signed in.
</p>
<p>Session expiry: {$page.data.session?.expires}</p>
<p>User: {user?.name} ({user?.email})</p>
14 changes: 0 additions & 14 deletions apps/dev/sveltekit/src/routes/protected/+page.svelte

This file was deleted.