Setting session/cookie after OAuth redirect

I am using sveltekit, my handle hook is this
export async function handle({ event, resolve }) {
const res = await auth.api.signInSocial({
body: {
provider: 'github',
callbackURL: '/'
}
});

redirect(302, res.url!);
}
export async function handle({ event, resolve }) {
const res = await auth.api.signInSocial({
body: {
provider: 'github',
callbackURL: '/'
}
});

redirect(302, res.url!);
}
This redirects users instantly to login when hitting the page. However, after logging in, how am I supposed to use the callback URL to authenticate the user? I don't find anything in the docs about this in the OAuth section. I dont want to use client side auth, I want to be able to handle the callback URL on the server directly but I'm having a hard time figuring out how? Typical OAuth flow usually means getting the JWT and RFT from the callback and setting them as cookies, but I still need to set up my user in database and stuff. Are there any examples of this?
22 Replies
xamarot
xamarotOP2mo ago
I simply want every non authed user to be forced to login and not able to access anything on the page before they do. But I can't find much beside doing "manual" logins using the client library
xamarot
xamarotOP2mo ago
Something close to what is described here: https://lucia-auth.com/tutorials/github-oauth/sveltekit
Lucia
Tutorial: GitHub OAuth in SvelteKit
An open source resource on implementing authentication with JavaScript
bekacru
bekacru2mo ago
better auth handles the callback itself. It'll create the user if not already registed, issues cookie and redirect the user to the callback url. you can try to intercept the calls through hooks if you need to do something additional but the base case is already covered.
xamarot
xamarotOP2mo ago
But I need to check the cookie in my hooks somehow, otherwise i will keep getting redirected to my login? I can't find anything in the docs on how to do that using just server side auth API.
bekacru
bekacru2mo ago
you can use auth.api.getSession to check if the user has a session or not make sure to pass the headers
xamarot
xamarotOP2mo ago
I tried that, but session is null. I can try again and see
bekacru
bekacru2mo ago
make sure the headers contains a cookie
xamarot
xamarotOP2mo ago
import { auth } from '$lib/server/auth'; // path to your auth file
import { redirect } from '@sveltejs/kit';
import { svelteKitHandler } from 'better-auth/svelte-kit';

export async function handle({ event, resolve }) {
const headers = event.request.headers;

console.dir(headers);

const session = await auth.api.getSession({
headers
});

console.dir(session);

const res = await auth.api.signInSocial({
body: {
provider: 'github',
callbackURL: '/'
}
});

redirect(302, res.url!);
}
import { auth } from '$lib/server/auth'; // path to your auth file
import { redirect } from '@sveltejs/kit';
import { svelteKitHandler } from 'better-auth/svelte-kit';

export async function handle({ event, resolve }) {
const headers = event.request.headers;

console.dir(headers);

const session = await auth.api.getSession({
headers
});

console.dir(session);

const res = await auth.api.signInSocial({
body: {
provider: 'github',
callbackURL: '/'
}
});

redirect(302, res.url!);
}
headers is an empty object and session is null after login callback am I using the svelteKitHandler correctly? because I feel like i should use it if I have the session, otherwise redirect
bekacru
bekacru2mo ago
make sure after you're redirected from github a session cookie is set in your browser. Also have you mounted the route handler? You need that so it can handle the callback
xamarot
xamarotOP2mo ago
like an /api/auth/callback/github? Or something else?
bekacru
bekacru2mo ago
no mount the handler auth.handler on hook.server.ts or on /api/auth/* route
xamarot
xamarotOP2mo ago
so, returning ´return svelteKitHandler({ event, resolve, auth });' in hooks and auth endpoint?
bekacru
bekacru2mo ago
yeah
xamarot
xamarotOP2mo ago
how do I do that in a server endpoint? AFAIK "resolve" does not exist there in the next docs it tells you to do export const { GET, POST } = toNextJsHandler(auth.handler); but SvelteKit has no examples and this const { GET, POST } = toSvelteKitHandler(auth.handler); does not seem to work
bekacru
bekacru2mo ago
you can export the auth.handler it self as GET and POST
const handler = auth.handler;

export { handler as GET, handler as POST}
const handler = auth.handler;

export { handler as GET, handler as POST}
xamarot
xamarotOP2mo ago
So now my hooks is this:
export async function handle({ event }) {
const headers = event.request.headers;

console.dir(headers);

const session = await auth.api.getSession({
headers
});

console.dir(session);

const res = await auth.api.signInSocial({
body: {
provider: 'github',
callbackURL: '/'
}
});

redirect(302, res.url!);
}
export async function handle({ event }) {
const headers = event.request.headers;

console.dir(headers);

const session = await auth.api.getSession({
headers
});

console.dir(session);

const res = await auth.api.signInSocial({
body: {
provider: 'github',
callbackURL: '/'
}
});

redirect(302, res.url!);
}
and /api/auth/callback/github/+server.ts
import { auth } from '$lib/server/auth';
const handler = auth.handler;
export { handler as GET, handler as POST}
import { auth } from '$lib/server/auth';
const handler = auth.handler;
export { handler as GET, handler as POST}
session and headers are still null and I keep just getting redirected to my login it seems like session should at least be present in hooks after the first callback to the +server endpoint?
bekacru
bekacru2mo ago
you should mount it on /api/auth/[...all]/+server.ts
xamarot
xamarotOP2mo ago
it still just keeps redirecting
bekacru
bekacru2mo ago
check if /api/auth/ok is returning a 200 response to check if it's mounted properly. Also don't redirect to github login on a hook. You shuld only redirect to the signin page
xamarot
xamarotOP2mo ago
What do you mean? Are login and signin different pages? And I can't see any 200 coming from my localhost, only 302s are being returned And if I want to redirect all users to sign up (don't want any unauthorized users reach any page of my app) how should I do it if not redirecting to the sign in?
bekacru
bekacru2mo ago
I'm saying make a sign in page where they can select sign in with github instead of automatically redirecting them go to your browser and navigate to http://localhost:5173/api/auth/ok and see what it returns. If it's mounted properly it should return {ok: true} and remove the sign-in redirection from every route since you don't want to redirect to github when the callback is reached as well.
xamarot
xamarotOP2mo ago
It throws an error, saying "TypeError: Cannot read properties of undefined (reading 'get')". Server endpoint looks like
import { auth } from '$lib/server/auth';

const handler = auth.handler;
export { handler as GET, handler as POST }
import { auth } from '$lib/server/auth';

const handler = auth.handler;
export { handler as GET, handler as POST }
Ok, now it works:
import { auth } from "$lib/server/auth";
import { toSvelteKitHandler } from "better-auth/svelte-kit";

const handler = toSvelteKitHandler(auth);
export { handler as GET, handler as POST }
import { auth } from "$lib/server/auth";
import { toSvelteKitHandler } from "better-auth/svelte-kit";

const handler = toSvelteKitHandler(auth);
export { handler as GET, handler as POST }
However, I am still confused. How do I handle redirecting users to login if not in hooks? Seems to me like something like this should work?
import { auth } from '$lib/server/auth'; // path to your auth file
import { redirect } from '@sveltejs/kit';
import { svelteKitHandler } from 'better-auth/svelte-kit';

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

if (session) {
return svelteKitHandler({ auth, event, resolve })
} else {
const res = await auth.api.signInSocial({
body: {
provider: 'github',
callbackURL: '/'
}
});

redirect(302, res.url!);
}
}

export function handleError({ error }) {
console.error(error);
}
import { auth } from '$lib/server/auth'; // path to your auth file
import { redirect } from '@sveltejs/kit';
import { svelteKitHandler } from 'better-auth/svelte-kit';

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

if (session) {
return svelteKitHandler({ auth, event, resolve })
} else {
const res = await auth.api.signInSocial({
body: {
provider: 'github',
callbackURL: '/'
}
});

redirect(302, res.url!);
}
}

export function handleError({ error }) {
console.error(error);
}
But session is always null. However, if I disable the redirect, I can see that I get the session and all that. I just need to check it before checking if i should be redirecting? But I do not understand how Shouldnt the [...all] callback handle setting the session before the app is again redirected back from the OAuth sign in? Is it possible i need a special case for when the server request is aimed at the auth callback? Sorry for spam but I think it's working now
import { auth } from '$lib/server/auth'; // path to your auth file
import { redirect, type Handle } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks';
import { svelteKitHandler } from 'better-auth/svelte-kit';

const setSessionHook: Handle = async ({ event, resolve }) => {
// this hooks needs to run first in order to set session properly before running the next hook
return svelteKitHandler({ auth, event, resolve })
};

const checkAuthHook: Handle = async ({ event, resolve }) => {
// if user is signed in, session should be here if the setSessionHook hook has been run
const headers = event.request.headers;
const session = await auth.api.getSession({
headers
});

if (!session) {
// redirect if no session is found
const res = await auth.api.signInSocial({
body: {
provider: 'github',
callbackURL: '/'
}
});

redirect(302, res.url!);
}

// resolve as usual
return await resolve(event);
}

export function handleError({ error }) {
console.error(error);
}

export const handle = sequence(setSessionHook, checkAuthHook);
import { auth } from '$lib/server/auth'; // path to your auth file
import { redirect, type Handle } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks';
import { svelteKitHandler } from 'better-auth/svelte-kit';

const setSessionHook: Handle = async ({ event, resolve }) => {
// this hooks needs to run first in order to set session properly before running the next hook
return svelteKitHandler({ auth, event, resolve })
};

const checkAuthHook: Handle = async ({ event, resolve }) => {
// if user is signed in, session should be here if the setSessionHook hook has been run
const headers = event.request.headers;
const session = await auth.api.getSession({
headers
});

if (!session) {
// redirect if no session is found
const res = await auth.api.signInSocial({
body: {
provider: 'github',
callbackURL: '/'
}
});

redirect(302, res.url!);
}

// resolve as usual
return await resolve(event);
}

export function handleError({ error }) {
console.error(error);
}

export const handle = sequence(setSessionHook, checkAuthHook);
I would like to submit this example to the docs if possible? Might be good for future users of sveltekit and betterauth

Did you find this page helpful?