Skip to content

Instantly share code, notes, and snippets.

@Maxiviper117
Created February 28, 2025 08:58
Show Gist options
  • Save Maxiviper117/71d21516a4f5ab4ba601ae90e2588def to your computer and use it in GitHub Desktop.
Save Maxiviper117/71d21516a4f5ab4ba601ae90e2588def to your computer and use it in GitHub Desktop.
CSRF Token setup in Sveltekit

CSRF Token Setup Using Root Layout’s Server Load Function

This guide shows you how to generate a CSRF token once in your root layout so that every page automatically receives the token, reducing duplication and ensuring consistency across your SvelteKit app.


1. Generate and Store the CSRF Token in +layout.server.ts

Create (or update) your root layout server file (+layout.server.ts) to check for an existing CSRF token in cookies. If not present, generate a new token using Node’s crypto module and set it in a cookie:

// +layout.server.ts
import { randomBytes } from 'crypto';
import type { LayoutServerLoad } from './$types';

export const load: LayoutServerLoad = async ({ cookies }) => {
	let csrfToken = cookies.get('csrfToken');
	if (!csrfToken) {
		csrfToken = randomBytes(32).toString('hex');
		// Not HttpOnly so that client-side code can access it for embedding in forms
		cookies.set('csrfToken', csrfToken, { path: '/' });
	}
	return { csrfToken };
};

Explanation:

  • We use randomBytes to generate a secure, random token.
  • The token is saved in a cookie (accessible by client-side code) so that it persists across requests.
  • The token is returned as part of the layout load data, making it available on every page.

2. Access the CSRF Token in Your Root Layout Component

In your root layout file (+layout.svelte), expose the token via the $page.data store:

<!-- +layout.svelte -->
<script lang="ts">
	export let data: { csrfToken: string };
</script>

<slot />

This file simply passes the data (including the CSRF token) to child components and pages.


3. Embed the CSRF Token in Your Forms

Now, in any page or component that contains a form, include the CSRF token in a hidden field. Since the token is available via $page.data, you can do something like:

<!-- ExampleForm.svelte -->
<script lang="ts">
	import { page } from '$app/state';
</script>

<form method="POST">
	<input type="hidden" name="csrfToken" value={page.data.csrfToken} />
	<!-- other form fields -->
	<button type="submit">Submit</button>
</form>

Explanation:

  • The hidden field named csrfToken includes the token from the root layout, ensuring every form submission carries the token for validation.

4. Validate the CSRF Token in Your Form Action

In your form’s action (for example, in +page.server.ts), compare the token submitted from the form with the one stored in the cookies:

// +page.server.ts
import { error } from '@sveltejs/kit';
import type { Actions } from './$types';

export const actions: Actions = {
	default: async ({ request, cookies }) => {
		const formData = await request.formData();
		const tokenFromForm = formData.get('csrfToken');
		const tokenFromCookie = cookies.get('csrfToken');

		if (!tokenFromForm || tokenFromForm !== tokenFromCookie) {
			console.error('CSRF token mismatch');
			throw error(403, 'Invalid CSRF token');
		}

		// Process the form data here...
		return { success: true };
	}
};

Explanation:

  • On form submission, the action compares the submitted token with the token in the cookie.
  • If they don’t match, the request is rejected with a 403 error.
  • Otherwise, the form data is processed as usual.

Final Notes

  • Token Rotation:
    Consider rotating the token after a successful submission for extra security.

  • Reusable CSRF Component:
    If you have many forms, you might encapsulate the hidden input in a reusable component.

  • Security:
    Although this double-submit cookie pattern is widely used, always consider your application’s specific security needs when designing CSRF protection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment