svelteKitHandler does not populate event.locals with session data #1010

Closed
opened 2026-03-13 08:17:21 -05:00 by GiteaMirror · 13 comments
Owner

Originally created by @vialcollet on GitHub (Apr 8, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Set up a SvelteKit project with Better-Auth (v1.2.5)
  2. Configure the auth handler in hooks.server.ts using svelteKitHandler
  3. Create a protected route that requires authentication
  4. Authenticate a user and navigate to the protected route
  5. Observe that event.locals.user and event.locals.session are undefined unless manually populated

Current vs. Expected behavior

Current behavior:
The svelteKitHandler from Better-Auth does not automatically populate event.locals with user and session data. To make authentication work, these lines must be manually added to the handle function:

event.locals.user = session.user;
event.locals.session = session.session;

If these lines are commented out, session is created in the database but the application experiences authentication issues despite a valid session existing.

Expected behavior:
The svelteKitHandler should automatically populate event.locals.user and event.locals.session with the session data, as is typical for SvelteKit authentication libraries. This would eliminate the need for manual assignment and reduce the risk of authentication issues.

What version of Better Auth are you using?

1.2.5

Provide environment information

- Better-Auth version: 1.2.5
- SvelteKit version: 2.20.4
- Svelte version : 5.25.6
- Node.js version: 18.x (or your specific version)
- OS: WSL2
- Database: PostgreSQL with Drizzle ORM

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

export const auth = betterAuth({
  baseURL: "http://localhost:5173",
  secret: "***", // Redacted for security
  trustedOrigins: [
    "http://localhost:5173", 
    "/app/home",
    "/admin",
  ],
  database: drizzleAdapter(db, {
    provider: "pg",
    schema: schema,
  }),
  user: {
    additionalFields: {
      firstName: { type: "string", required: true, input: true },
      lastName: { type: "string", required: true, input: true }
    }
  },
  emailAndPassword: {
    enabled: true,
    fromEmail: "noreply@example.com",
    transport: { type: "debug" },
  },
  socialProviders: {
    google: {
      clientId: "***", // Redacted for security
      clientSecret: "***", // Redacted for security
    },
  },
  debug: process.env.NODE_ENV !== 'production',
});

Additional context

I've discovered that the official examples don't include these manual assignments to event.locals, which leads developers to incorrectly assume the handler will handle this automatically.

https://github.com/better-auth/better-auth/pull/1742

Interestingly, another developer had previously submitted a pull request to add exactly these manual assignments to the examples, confirming that multiple developers have independently encountered this issue. This suggests it's a common point of friction for new users of the library.

This issue could be resolved either by:

  1. Enhancing the svelteKitHandler to automatically populate event.locals with session data
  2. Clearly documenting in examples and docs that manual assignment is required
  3. Creating a higher-level helper function that handles this automatically

Addressing this would significantly improve the developer experience when integrating Better-Auth with SvelteKit.

Originally created by @vialcollet on GitHub (Apr 8, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Set up a SvelteKit project with Better-Auth (v1.2.5) 2. Configure the auth handler in hooks.server.ts using svelteKitHandler 3. Create a protected route that requires authentication 4. Authenticate a user and navigate to the protected route 5. Observe that event.locals.user and event.locals.session are undefined unless manually populated ### Current vs. Expected behavior **Current behavior:** The `svelteKitHandler` from Better-Auth does not automatically populate `event.locals` with user and session data. To make authentication work, these lines must be manually added to the handle function: ```typescript event.locals.user = session.user; event.locals.session = session.session; ``` If these lines are commented out, session is created in the database but the application experiences authentication issues despite a valid session existing. **Expected behavior:** The `svelteKitHandler` should automatically populate `event.locals.user` and `event.locals.session` with the session data, as is typical for SvelteKit authentication libraries. This would eliminate the need for manual assignment and reduce the risk of authentication issues. ### What version of Better Auth are you using? 1.2.5 ### Provide environment information ```bash - Better-Auth version: 1.2.5 - SvelteKit version: 2.20.4 - Svelte version : 5.25.6 - Node.js version: 18.x (or your specific version) - OS: WSL2 - Database: PostgreSQL with Drizzle ORM ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript export const auth = betterAuth({ baseURL: "http://localhost:5173", secret: "***", // Redacted for security trustedOrigins: [ "http://localhost:5173", "/app/home", "/admin", ], database: drizzleAdapter(db, { provider: "pg", schema: schema, }), user: { additionalFields: { firstName: { type: "string", required: true, input: true }, lastName: { type: "string", required: true, input: true } } }, emailAndPassword: { enabled: true, fromEmail: "noreply@example.com", transport: { type: "debug" }, }, socialProviders: { google: { clientId: "***", // Redacted for security clientSecret: "***", // Redacted for security }, }, debug: process.env.NODE_ENV !== 'production', }); ``` ### Additional context I've discovered that the official examples don't include these manual assignments to `event.locals`, which leads developers to incorrectly assume the handler will handle this automatically. https://github.com/better-auth/better-auth/pull/1742 Interestingly, another developer had previously submitted a pull request to add exactly these manual assignments to the examples, confirming that multiple developers have independently encountered this issue. This suggests it's a common point of friction for new users of the library. This issue could be resolved either by: 1. Enhancing the `svelteKitHandler` to automatically populate `event.locals` with session data 2. Clearly documenting in examples and docs that manual assignment is required 3. Creating a higher-level helper function that handles this automatically Addressing this would significantly improve the developer experience when integrating Better-Auth with SvelteKit.
GiteaMirror added the documentation label 2026-03-13 08:17:21 -05:00
Author
Owner

@thomasmol commented on GitHub (Apr 16, 2025):

Great find, couldn't figure out in the beginning why everything was undefined 😁
We should also find a way to make sure the types are correctly inferred for the locals.session and locals.user

@thomasmol commented on GitHub (Apr 16, 2025): Great find, couldn't figure out in the beginning why everything was undefined 😁 We should also find a way to make sure the types are correctly inferred for the locals.session and locals.user
Author
Owner

@AgungBahtiarr commented on GitHub (Apr 23, 2025):

Is this suited for github?

* [x]  Yes, this is suited for github

To Reproduce

1. Set up a SvelteKit project with Better-Auth (v1.2.5)

2. Configure the auth handler in hooks.server.ts using svelteKitHandler

3. Create a protected route that requires authentication

4. Authenticate a user and navigate to the protected route

5. Observe that event.locals.user and event.locals.session are undefined unless manually populated

Current vs. Expected behavior

Current behavior: The svelteKitHandler from Better-Auth does not automatically populate event.locals with user and session data. To make authentication work, these lines must be manually added to the handle function:

event.locals.user = session.user;
event.locals.session = session.session;

If these lines are commented out, session is created in the database but the application experiences authentication issues despite a valid session existing.

Expected behavior: The svelteKitHandler should automatically populate event.locals.user and event.locals.session with the session data, as is typical for SvelteKit authentication libraries. This would eliminate the need for manual assignment and reduce the risk of authentication issues.

What version of Better Auth are you using?

1.2.5

Provide environment information

  • Better-Auth version: 1.2.5
  • SvelteKit version: 2.20.4
  • Svelte version : 5.25.6
  • Node.js version: 18.x (or your specific version)
  • OS: WSL2
  • Database: PostgreSQL with Drizzle ORM

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

export const auth = betterAuth({
baseURL: "http://localhost:5173",
secret: "", // Redacted for security
trustedOrigins: [
"http://localhost:5173",
"/app/home",
"/admin",
],
database: drizzleAdapter(db, {
provider: "pg",
schema: schema,
}),
user: {
additionalFields: {
firstName: { type: "string", required: true, input: true },
lastName: { type: "string", required: true, input: true }
}
},
emailAndPassword: {
enabled: true,
fromEmail: "noreply@example.com",
transport: { type: "debug" },
},
socialProviders: {
google: {
clientId: "
", // Redacted for security
clientSecret: "***", // Redacted for security
},
},
debug: process.env.NODE_ENV !== 'production',
});

Additional context

I've discovered that the official examples don't include these manual assignments to event.locals, which leads developers to incorrectly assume the handler will handle this automatically.

#1742

Interestingly, another developer had previously submitted a pull request to add exactly these manual assignments to the examples, confirming that multiple developers have independently encountered this issue. This suggests it's a common point of friction for new users of the library.

This issue could be resolved either by:

1. Enhancing the `svelteKitHandler` to automatically populate `event.locals` with session data

2. Clearly documenting in examples and docs that manual assignment is required

3. Creating a higher-level helper function that handles this automatically

Addressing this would significantly improve the developer experience when integrating Better-Auth with SvelteKit.

hello sir, i still cant fix this issue, here is my code
`import { auth } from '$lib/auth';
import { redirect, type Handle } from '@sveltejs/kit';
import { svelteKitHandler } from 'better-auth/svelte-kit';

export const handle: Handle = async ({ event, resolve }) => {
const session = await auth.api.getSession({
headers: event.request.headers
});

event.locals.user = session?.user;
event.locals.session = session?.session;

return svelteKitHandler({ event, resolve, auth });

};
`

anything wrong?

@AgungBahtiarr commented on GitHub (Apr 23, 2025): > ### Is this suited for github? > > * [x] Yes, this is suited for github > > > ### To Reproduce > > 1. Set up a SvelteKit project with Better-Auth (v1.2.5) > > 2. Configure the auth handler in hooks.server.ts using svelteKitHandler > > 3. Create a protected route that requires authentication > > 4. Authenticate a user and navigate to the protected route > > 5. Observe that event.locals.user and event.locals.session are undefined unless manually populated > > > ### Current vs. Expected behavior > > **Current behavior:** The `svelteKitHandler` from Better-Auth does not automatically populate `event.locals` with user and session data. To make authentication work, these lines must be manually added to the handle function: > > event.locals.user = session.user; > event.locals.session = session.session; > > If these lines are commented out, session is created in the database but the application experiences authentication issues despite a valid session existing. > > **Expected behavior:** The `svelteKitHandler` should automatically populate `event.locals.user` and `event.locals.session` with the session data, as is typical for SvelteKit authentication libraries. This would eliminate the need for manual assignment and reduce the risk of authentication issues. > ### What version of Better Auth are you using? > > 1.2.5 > ### Provide environment information > > - Better-Auth version: 1.2.5 > - SvelteKit version: 2.20.4 > - Svelte version : 5.25.6 > - Node.js version: 18.x (or your specific version) > - OS: WSL2 > - Database: PostgreSQL with Drizzle ORM > > ### Which area(s) are affected? (Select all that apply) > > Backend > ### Auth config (if applicable) > > export const auth = betterAuth({ > baseURL: "http://localhost:5173", > secret: "***", // Redacted for security > trustedOrigins: [ > "http://localhost:5173", > "/app/home", > "/admin", > ], > database: drizzleAdapter(db, { > provider: "pg", > schema: schema, > }), > user: { > additionalFields: { > firstName: { type: "string", required: true, input: true }, > lastName: { type: "string", required: true, input: true } > } > }, > emailAndPassword: { > enabled: true, > fromEmail: "noreply@example.com", > transport: { type: "debug" }, > }, > socialProviders: { > google: { > clientId: "***", // Redacted for security > clientSecret: "***", // Redacted for security > }, > }, > debug: process.env.NODE_ENV !== 'production', > }); > > ### Additional context > > I've discovered that the official examples don't include these manual assignments to `event.locals`, which leads developers to incorrectly assume the handler will handle this automatically. > > [#1742](https://github.com/better-auth/better-auth/pull/1742) > > Interestingly, another developer had previously submitted a pull request to add exactly these manual assignments to the examples, confirming that multiple developers have independently encountered this issue. This suggests it's a common point of friction for new users of the library. > > This issue could be resolved either by: > > 1. Enhancing the `svelteKitHandler` to automatically populate `event.locals` with session data > > 2. Clearly documenting in examples and docs that manual assignment is required > > 3. Creating a higher-level helper function that handles this automatically > > > Addressing this would significantly improve the developer experience when integrating Better-Auth with SvelteKit. hello sir, i still cant fix this issue, here is my code `import { auth } from '$lib/auth'; import { redirect, type Handle } from '@sveltejs/kit'; import { svelteKitHandler } from 'better-auth/svelte-kit'; export const handle: Handle = async ({ event, resolve }) => { const session = await auth.api.getSession({ headers: event.request.headers }); event.locals.user = session?.user; event.locals.session = session?.session; return svelteKitHandler({ event, resolve, auth }); }; ` anything wrong?
Author
Owner

@thomasmol commented on GitHub (Apr 23, 2025):

@AgungBahtiarr what issue? your code looks correct though.

Edit:
I see your issue now. You first need to call the svelteKitHandler, and then populate the locals. Otherwise they'll always be empty.
This is how i am using it in my hooks.server.ts:

import { svelteKitHandler } from 'better-auth/svelte-kit';
import { auth as betterAuth } from '$lib/auth';
import { sequence } from '@sveltejs/kit/hooks';

const betterAuthHandler = (async ({ event, resolve }) => {
	return svelteKitHandler({
		event,
		resolve,
		auth: betterAuth
	});
}) satisfies Handle;

const betterAuthSessionHandler = (async ({ event, resolve }) => {
	const session = await betterAuth.api.getSession({
		headers: event.request.headers
	});
	event.locals.session = session?.session;
	event.locals.user = session?.user;

	return resolve(event);
}) satisfies Handle;

export const handle = sequence(
	betterAuthHandler,
	betterAuthSessionHandler
) satisfies Handle;

Notice the order the handlers are executed in the sequence function

@thomasmol commented on GitHub (Apr 23, 2025): @AgungBahtiarr what issue? your code looks correct though. Edit: I see your issue now. You first need to call the `svelteKitHandler`, and then populate the `locals`. Otherwise they'll always be empty. This is how i am using it in my `hooks.server.ts`: ```typescript hooks.server.ts import { svelteKitHandler } from 'better-auth/svelte-kit'; import { auth as betterAuth } from '$lib/auth'; import { sequence } from '@sveltejs/kit/hooks'; const betterAuthHandler = (async ({ event, resolve }) => { return svelteKitHandler({ event, resolve, auth: betterAuth }); }) satisfies Handle; const betterAuthSessionHandler = (async ({ event, resolve }) => { const session = await betterAuth.api.getSession({ headers: event.request.headers }); event.locals.session = session?.session; event.locals.user = session?.user; return resolve(event); }) satisfies Handle; export const handle = sequence( betterAuthHandler, betterAuthSessionHandler ) satisfies Handle; ``` Notice the order the handlers are executed in the `sequence` function
Author
Owner

@AgungBahtiarr commented on GitHub (Apr 23, 2025):

Thank you for your help sir, I have tried the code you provided, but I still get null when calling session, maybe for more details here is my repo https://github.com/AgungBahtiarr/laros-finance

@AgungBahtiarr commented on GitHub (Apr 23, 2025): Thank you for your help sir, I have tried the code you provided, but I still get null when calling session, maybe for more details here is my repo https://github.com/AgungBahtiarr/laros-finance
Author
Owner

@vladshcherbin commented on GitHub (May 13, 2025):

I see your issue now. You first need to call the svelteKitHandler, and then populate the locals. Otherwise they'll always be empty.

That's not true.

@vladshcherbin commented on GitHub (May 13, 2025): > I see your issue now. You first need to call the `svelteKitHandler`, and then populate the `locals`. Otherwise they'll always be empty. That's not true.
Author
Owner

@thomasmol commented on GitHub (May 13, 2025):

I see your issue now. You first need to call the svelteKitHandler, and then populate the locals. Otherwise they'll always be empty.

That's not true.

Please elaborate why @vladshcherbin

@thomasmol commented on GitHub (May 13, 2025): > > I see your issue now. You first need to call the `svelteKitHandler`, and then populate the `locals`. Otherwise they'll always be empty. > > That's not true. Please elaborate why @vladshcherbin
Author
Owner

@vladshcherbin commented on GitHub (May 13, 2025):

@thomasmol once you get session via auth.api.getSession and set event.locals, they won't be empty. Example:

export const handle: Handle = async ({ event, resolve }) => {
  const session = await auth.api.getSession({
    headers: event.request.headers
  })

  event.locals.user = session?.user ?? null

  return svelteKitHandler({ auth, event, resolve })
}
@vladshcherbin commented on GitHub (May 13, 2025): @thomasmol once you get session via `auth.api.getSession` and set `event.locals`, they won't be empty. Example: ```ts export const handle: Handle = async ({ event, resolve }) => { const session = await auth.api.getSession({ headers: event.request.headers }) event.locals.user = session?.user ?? null return svelteKitHandler({ auth, event, resolve }) } ```
Author
Owner

@thomasmol commented on GitHub (May 13, 2025):

@vladshcherbin you are correct.

you don't need to to do ?? null though since session?.user is already typed as User | null

const betterAuthHandler = (async ({ event, resolve }) => {
	const session = await betterAuth.api.getSession({
		headers: event.request.headers
	});
	event.locals.session = session?.session;
	event.locals.user = session?.user;
	
	return svelteKitHandler({
		event,
		resolve,
		auth: betterAuth
	});
}) satisfies Handle;

should do the trick

@thomasmol commented on GitHub (May 13, 2025): @vladshcherbin you are correct. you don't need to to do `?? null` though since `session?.user` is already typed as `User | null` ```typescript const betterAuthHandler = (async ({ event, resolve }) => { const session = await betterAuth.api.getSession({ headers: event.request.headers }); event.locals.session = session?.session; event.locals.user = session?.user; return svelteKitHandler({ event, resolve, auth: betterAuth }); }) satisfies Handle; ``` should do the trick
Author
Owner

@rebasecase commented on GitHub (Jun 26, 2025):

session?.user is User | undefined, I'm also not certain if getSession will work in all instances if it is called prior to svelteKitHandler - I like your first method better.

You'll need to update app.d.ts with something like

interface Locals {
  session?: import('$lib/auth').Session;
  user?: import('$lib/auth').User;
}

assuming

export type Session = typeof auth.$Infer.Session.session;
export type User = typeof auth.$Infer.Session.user;

Completely agree that svelteKitHandler should do this for you.

@rebasecase commented on GitHub (Jun 26, 2025): `session?.user` is `User | undefined`, I'm also not certain if `getSession` will work in all instances if it is called prior to `svelteKitHandler` - I like your first method better. You'll need to update `app.d.ts` with something like ```ts interface Locals { session?: import('$lib/auth').Session; user?: import('$lib/auth').User; } ``` assuming ```ts export type Session = typeof auth.$Infer.Session.session; export type User = typeof auth.$Infer.Session.user; ``` Completely agree that `svelteKitHandler` should do this for you.
Author
Owner

@officialankan commented on GitHub (Jul 28, 2025):

This would be a great add either to the svelteKitHandler or the related docs.

Also, I don't quite understand in your discussion here why you have to chain two handles in sequence? What is wrong with

import { auth } from "$lib/server/auth";
import { svelteKitHandler } from "better-auth/svelte-kit";
import { building } from "$app/environment";
import type { Handle } from "@sveltejs/kit";

export const handle: Handle = async ({ event, resolve }) => {
	// check for auth session
	const res = await auth.api.getSession({ headers: event.request.headers });
	event.locals.session = res?.session
	event.locals.user = res?.user

	return svelteKitHandler({ event, resolve, auth, building });
};

assuming

import type { Session, User } from "better-auth";

// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
declare global {
	namespace App {
		// interface Error {}
		interface Locals {
			session: Session | undefined;
			user: User | undefined;
		}
		// interface PageData {}
		// interface PageState {}
		// interface Platform {}
	}
}

export {};
@officialankan commented on GitHub (Jul 28, 2025): This would be a great add either to the `svelteKitHandler` or the related docs. Also, I don't quite understand in your discussion here why you have to chain two handles in sequence? What is wrong with ```typescript import { auth } from "$lib/server/auth"; import { svelteKitHandler } from "better-auth/svelte-kit"; import { building } from "$app/environment"; import type { Handle } from "@sveltejs/kit"; export const handle: Handle = async ({ event, resolve }) => { // check for auth session const res = await auth.api.getSession({ headers: event.request.headers }); event.locals.session = res?.session event.locals.user = res?.user return svelteKitHandler({ event, resolve, auth, building }); }; ``` assuming ```typescript import type { Session, User } from "better-auth"; // See https://svelte.dev/docs/kit/types#app.d.ts // for information about these interfaces declare global { namespace App { // interface Error {} interface Locals { session: Session | undefined; user: User | undefined; } // interface PageData {} // interface PageState {} // interface Platform {} } } export {}; ```
Author
Owner

@ziahm6638 commented on GitHub (Aug 7, 2025):

To ensure cookies are properly set when you call functions like signInEmail or signUpEmail in a server action, you should use the sveltekitCookies plugin. This plugin will automatically handle setting cookies for you in SvelteKit.

You need to add it as a plugin to your Better Auth instance.

in your lib/auth.ts

import { sveltekitCookies } from "better-auth/svelte-kit";
import { getRequestEvent } from "$app/server";

export const auth = betterAuth({
// ... your config
plugins: [sveltekitCookies(getRequestEvent)],
});

The getRequestEvent function is available in SvelteKit 2.2.0 and later. Make sure you are using a compatible version.

@ziahm6638 commented on GitHub (Aug 7, 2025): To ensure cookies are properly set when you call functions like signInEmail or signUpEmail in a server action, you should use the sveltekitCookies plugin. This plugin will automatically handle setting cookies for you in SvelteKit. You need to add it as a plugin to your Better Auth instance. in your lib/auth.ts import { sveltekitCookies } from "better-auth/svelte-kit"; import { getRequestEvent } from "$app/server"; export const auth = betterAuth({ // ... your config plugins: [sveltekitCookies(getRequestEvent)], }); The getRequestEvent function is available in SvelteKit 2.2.0 and later. Make sure you are using a compatible version.
Author
Owner

@thomasmol commented on GitHub (Aug 7, 2025):

@ziahm6638 thanks Ziahm. However, it is unrelated to this issue and wouldn't solve this problem.

@thomasmol commented on GitHub (Aug 7, 2025): @ziahm6638 thanks Ziahm. However, it is unrelated to this issue and wouldn't solve this problem.
Author
Owner

@dosubot[bot] commented on GitHub (Nov 6, 2025):

Hi, @vialcollet. I'm Dosu, and I'm helping the better-auth team manage their backlog and am marking this issue as stale.

Issue Summary:

  • You reported that svelteKitHandler (v1.2.5) does not auto-populate event.locals with user and session data, requiring manual assignment.
  • Contributors clarified that this manual step is currently necessary and not a bug.
  • Discussions included usage patterns, typing for locals, and cookie handling, with the core issue focused on manual assignment.
  • Suggestions were made to improve documentation and potentially add helper functions or enhance svelteKitHandler in the future.

Next Steps:

  • Please confirm if this behavior is still relevant with the latest version of better-auth by commenting on this issue.
  • If I do not receive a response, I will automatically close the issue in 7 days.

Thanks for your understanding and contribution!

@dosubot[bot] commented on GitHub (Nov 6, 2025): Hi, @vialcollet. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog and am marking this issue as stale. **Issue Summary:** - You reported that svelteKitHandler (v1.2.5) does not auto-populate event.locals with user and session data, requiring manual assignment. - Contributors clarified that this manual step is currently necessary and not a bug. - Discussions included usage patterns, typing for locals, and cookie handling, with the core issue focused on manual assignment. - Suggestions were made to improve documentation and potentially add helper functions or enhance svelteKitHandler in the future. **Next Steps:** - Please confirm if this behavior is still relevant with the latest version of better-auth by commenting on this issue. - If I do not receive a response, I will automatically close the issue in 7 days. Thanks for your understanding and contribution!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1010