Feat: Add a SvelteKit cookie helper #251

Closed
opened 2026-03-13 07:39:46 -05:00 by GiteaMirror · 10 comments
Owner

Originally created by @Devr-pro on GitHub (Nov 20, 2024).

Is your feature request related to a problem? Please describe.
Currently, SvelteKit integration has no cookie helper like nextjs

Describe the solution you'd like
Similar plugin or helper for the SvelteKit, which would make server auth with server actions pretty simple and straightforward

Describe alternatives you've considered
Rather than a plugin maybe just a helper function can be provided and documented how to use in integrations docs (this can be just alternative as better-auth provides plugins feature without additional work)

Additional context
Right now to get things working, we have to parse the cookie, set appropriate config options and then set it.

Originally created by @Devr-pro on GitHub (Nov 20, 2024). **Is your feature request related to a problem? Please describe.** Currently, SvelteKit integration has no cookie helper like [nextjs](https://www.better-auth.com/docs/integrations/next#server-action-cookies) **Describe the solution you'd like** Similar plugin or helper for the SvelteKit, which would make server auth with server actions pretty simple and straightforward **Describe alternatives you've considered** Rather than a plugin maybe just a helper function can be provided and documented how to use in integrations docs (this can be just alternative as better-auth provides plugins feature without additional work) **Additional context** Right now to get things working, we have to parse the cookie, set appropriate config options and then set it.
GiteaMirror added the enhancement label 2026-03-13 07:39:46 -05:00
Author
Owner

@bienbiendev commented on GitHub (Jan 24, 2025):

I am trying to implement this in a SvelteKit route action without success.
This is my current code :

// in an action handler
const signin = await auth.api.signInEmail({
  body: {
    email,
    password
  },
  asResponse: true
});
const setCookieHeader = signin.headers.get('set-cookie');
if (setCookieHeader) {
  const parsedCookie = setCookieHeader.split(';')[0];
  const [name, value] = parsedCookie.split('=');
  cookies.set(name, value, {
    path: '/',
    httpOnly: true,
    sameSite: 'lax',
    maxAge: 604800,
    secure: !dev
  });
}

However then when requesting a route ex: /admin and checking for the session in my handler :

// hooks.server.ts
let authenticated = await auth.api.getSession({
  headers: event.request.headers // contain the better-auth.session_token previously added
});

Even if the better-auth.session_token cookie is set and equla to the one added in the action handler, authenticated is null.

I probably miss something.

@bienbiendev commented on GitHub (Jan 24, 2025): I am trying to implement this in a SvelteKit route action without success. This is my current code : ```js // in an action handler const signin = await auth.api.signInEmail({ body: { email, password }, asResponse: true }); const setCookieHeader = signin.headers.get('set-cookie'); if (setCookieHeader) { const parsedCookie = setCookieHeader.split(';')[0]; const [name, value] = parsedCookie.split('='); cookies.set(name, value, { path: '/', httpOnly: true, sameSite: 'lax', maxAge: 604800, secure: !dev }); } ``` However then when requesting a route ex: /admin and checking for the session in my handler : ```js // hooks.server.ts let authenticated = await auth.api.getSession({ headers: event.request.headers // contain the better-auth.session_token previously added }); ``` Even if the `better-auth.session_token` cookie is set and equla to the one added in the action handler, `authenticated` is `null`. I probably miss something.
Author
Owner

@bienbiendev commented on GitHub (Jan 24, 2025):

So actually the problem was that the set-cookie value returned by auth.api.signInEmail, is encoded.
Then when you set the cookie with the svelteKit cookies.set it is encoded again so I need to decode the cookie before setting it :

// action handler
const signin = await auth.api.signInEmail({
  body: {
    email,
    password
  },
  asResponse: true
});
const setCookieHeader = signin.headers.get('set-cookie');
if (setCookieHeader) {
  const parsedCookie = setCookieHeader.split(';')[0];
  const [name, encodedValue] = parsedCookie.split('=');
  // need to decode it first
  const decodedValue = decodeURIComponent(encodedValue);
  cookies.set(name, decodedValue, {
    path: '/',
    httpOnly: true,
    sameSite: 'lax',
    maxAge: 604800,
    secure: !dev
  });
}

I missed the difference between these :
3TO13RG9zBVZi2oP9h5ehiVNK6mxMU3n.kZppr0OuhRwc42HV3DXQkWznSAHVt2g2xG84c0OWRzk%3D
and
3TO13RG9zBVZi2oP9h5ehiVNK6mxMU3n.kZppr0OuhRwc42HV3DXQkWznSAHVt2g2xG84c0OWRzk%253D

@bienbiendev commented on GitHub (Jan 24, 2025): So actually the problem was that the set-cookie value returned by `auth.api.signInEmail`, is encoded. Then when you set the cookie with the svelteKit `cookies.set` it is encoded again so I need to decode the cookie before setting it : ```js // action handler const signin = await auth.api.signInEmail({ body: { email, password }, asResponse: true }); const setCookieHeader = signin.headers.get('set-cookie'); if (setCookieHeader) { const parsedCookie = setCookieHeader.split(';')[0]; const [name, encodedValue] = parsedCookie.split('='); // need to decode it first const decodedValue = decodeURIComponent(encodedValue); cookies.set(name, decodedValue, { path: '/', httpOnly: true, sameSite: 'lax', maxAge: 604800, secure: !dev }); } ``` I missed the difference between these : `3TO13RG9zBVZi2oP9h5ehiVNK6mxMU3n.kZppr0OuhRwc42HV3DXQkWznSAHVt2g2xG84c0OWRzk%3D` and `3TO13RG9zBVZi2oP9h5ehiVNK6mxMU3n.kZppr0OuhRwc42HV3DXQkWznSAHVt2g2xG84c0OWRzk%253D`
Author
Owner

@DevDuki commented on GitHub (Feb 14, 2025):

Do you have to do the same when signing up with auth.api.signUpEmail?

@DevDuki commented on GitHub (Feb 14, 2025): Do you have to do the same when signing up with `auth.api.signUpEmail`?
Author
Owner

@bienbiendev commented on GitHub (Feb 14, 2025):

Do you have to do the same when signing up with auth.api.signUpEmail?

I don't auto signin when signup in my case, but if you need to set a cookie probably yes.
Depends on your context, and if you don't need to handle server side operations when signUp/signIn, client API is easier

@bienbiendev commented on GitHub (Feb 14, 2025): > Do you have to do the same when signing up with `auth.api.signUpEmail`? I don't auto signin when signup in my case, but if you need to set a cookie probably yes. Depends on your context, and **if you don't need to handle server side operations** when signUp/signIn, **client API is easier**
Author
Owner

@islamzaoui commented on GitHub (Feb 22, 2025):

Hi @bienoubien-studio, do you have any success with this? I can't migrate to better auth from Lucia unless this works

@islamzaoui commented on GitHub (Feb 22, 2025): Hi @bienoubien-studio, do you have any success with this? I can't migrate to better auth from Lucia unless this works
Author
Owner

@bienbiendev commented on GitHub (Mar 1, 2025):

Hi @bienoubien-studio, do you have any success with this? I can't migrate to better auth from Lucia unless this works

Follow my self reply and you should be good

@bienbiendev commented on GitHub (Mar 1, 2025): > Hi [@bienoubien-studio](https://github.com/bienoubien-studio), do you have any success with this? I can't migrate to better auth from Lucia unless this works Follow my self reply and you should be good
Author
Owner

@actuallyjamez commented on GitHub (Mar 22, 2025):

The svelte kit team recently released getRequestEvent in SvelteKit 2.20, so this could now be implemented as a plugin similar to nextCookies. Here's a quick example: gist.

@actuallyjamez commented on GitHub (Mar 22, 2025): The svelte kit team recently released [getRequestEvent](https://svelte.dev/docs/kit/$app-server#getRequestEvent) in SvelteKit 2.20, so this could now be implemented as a plugin similar to `nextCookies`. Here's a quick example: [gist](https://gist.github.com/actuallyjamez/d345f52b3ecaaebadd320f98909951d6).
Author
Owner

@benjaminmodayil commented on GitHub (Mar 22, 2025):

@bienoubien-studio I appreciate you! I didn't notice the encoding issue until seeing your post and your solution fixed things for me!

@benjaminmodayil commented on GitHub (Mar 22, 2025): @bienoubien-studio I appreciate you! I didn't notice the encoding issue until seeing your post and your solution fixed things for me!
Author
Owner

@arkmech commented on GitHub (Apr 15, 2025):

Any update on this?

@arkmech commented on GitHub (Apr 15, 2025): Any update on this?
Author
Owner

@posixpascal commented on GitHub (Jun 12, 2025):

I've done it using @actuallyjamez code using a hook. I'm using an enhanced form and handle auth on the server side using actions.

import { svelteKitHandler } from "better-auth/svelte-kit";
import { auth } from "$lib/auth";
import type { Handle } from '@sveltejs/kit';
import { sequence } from "@sveltejs/kit/hooks";

const authStateHandler: Handle = async ({ event, resolve, }) => {
    event.locals.user = undefined;
    event.locals.session = undefined;
    const sessionResult = await auth.api.getSession({
        headers: event.request.headers
    });
    
    if (sessionResult !== null){
        const { session, user } = sessionResult;
        event.locals.user = user;
        event.locals.session = session;
    }

    const response = await resolve(event);
    return response;
}

export const handle = sequence(/*... more handlers*/ authStateHandler)

And inside my page.server.ts:

export const actions = {
	default: async ({ request,  cookies }) => {
		const form = await superValidate(request, zod4(signInSchema));

		if (!form.valid) {
			return fail(400, { form });
		}

		try {
			const response = await auth.api.signInEmail({
				body: {
                    rememberMe: form.data.rememberMe,
					email: form.data.email,
					password: form.data.password
				},
                asResponse: true,
			});

            // https://github.com/better-auth/better-auth/issues/600
            type CookieAttributes = ReturnType<typeof parseSetCookieHeader> extends Map<string, infer V> ? V : never

           // >>> This is the actual cookie setting logic taken from @actuallyjamez
            const betterAuthCookies = response.headers.getSetCookie().reduce((acc: Map<string, CookieAttributes>, current) => {
                return new Map([...acc, ...parseSetCookieHeader(current)]);
            }, new Map());
            
			for (const [name, { value, ...ops }] of betterAuthCookies) {
				cookies.set(name, value, {
					sameSite: ops.samesite,
					path: ops.path || "/",
					expires: ops.expires,
					secure: ops.secure,
					httpOnly: ops.httponly,
					domain: ops.domain,
					maxAge: ops["max-age"],
					encode: (value) => value,
				});

             // <<<
            }
		} catch (err) {
			const error = err as Error;
			return message<ServerMessage>(form, { 
                text: error.message,
                status: "error"
            }, {
				status: 422
			});
		}

        redirect(303, '/');
	}  
};

I'm now logged in using sveltekit and better-auth.

@posixpascal commented on GitHub (Jun 12, 2025): I've done it using @actuallyjamez code using a hook. I'm using an enhanced form and handle auth on the server side using actions. ```ts import { svelteKitHandler } from "better-auth/svelte-kit"; import { auth } from "$lib/auth"; import type { Handle } from '@sveltejs/kit'; import { sequence } from "@sveltejs/kit/hooks"; const authStateHandler: Handle = async ({ event, resolve, }) => { event.locals.user = undefined; event.locals.session = undefined; const sessionResult = await auth.api.getSession({ headers: event.request.headers }); if (sessionResult !== null){ const { session, user } = sessionResult; event.locals.user = user; event.locals.session = session; } const response = await resolve(event); return response; } export const handle = sequence(/*... more handlers*/ authStateHandler) ``` And inside my page.server.ts: ```ts export const actions = { default: async ({ request, cookies }) => { const form = await superValidate(request, zod4(signInSchema)); if (!form.valid) { return fail(400, { form }); } try { const response = await auth.api.signInEmail({ body: { rememberMe: form.data.rememberMe, email: form.data.email, password: form.data.password }, asResponse: true, }); // https://github.com/better-auth/better-auth/issues/600 type CookieAttributes = ReturnType<typeof parseSetCookieHeader> extends Map<string, infer V> ? V : never // >>> This is the actual cookie setting logic taken from @actuallyjamez const betterAuthCookies = response.headers.getSetCookie().reduce((acc: Map<string, CookieAttributes>, current) => { return new Map([...acc, ...parseSetCookieHeader(current)]); }, new Map()); for (const [name, { value, ...ops }] of betterAuthCookies) { cookies.set(name, value, { sameSite: ops.samesite, path: ops.path || "/", expires: ops.expires, secure: ops.secure, httpOnly: ops.httponly, domain: ops.domain, maxAge: ops["max-age"], encode: (value) => value, }); // <<< } } catch (err) { const error = err as Error; return message<ServerMessage>(form, { text: error.message, status: "error" }, { status: 422 }); } redirect(303, '/'); } }; ``` I'm now logged in using sveltekit and better-auth.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#251