next-auth
next-auth copied to clipboard
SvelteKitAuth: How to update session from client to server and vice versa
What is the improvement or update you wish to see?
Scenario 1: Client to Server
In SvelteKitAuth, how to communicate session updates from client to server?
In NextAuth, we do something like:
const { data: session, status, update } = useSession()
<button onClick={() => update({ name: "John Doe" })}>Edit name</button>
Scenario 2: Server to Client
In SvelteKitAuth, we manage session within jwt({ ... })
and session({ ... })
callbacks. Let's consider a scenario in which user object was mutated by some event (like response from API call), how to update global session object in that case?
Is there any context that might help us understand?
The NextAuth has many helper methods exposed for both Client and Server API that makes it easy t implement above mentioned scenarios. How do we implement those in SvelteKitAuth?
Does the docs page already exist? Please link to it.
https://next-auth.js.org/getting-started/client#updating-the-session
@balazsorban44 @ThangHuuVu @ndom91
Seems like it's not implemented.
Okey, it's actually supported, you can use next function:
import { base } from '$app/paths';
import type { Session } from '@auth/core/types';
export async function updateSession(data: Session): Promise<Session | null> {
const sessionUrl = `${base}/auth/session`;
const csrfTokenResponse = await fetch(`${base}/auth/csrf`);
const { csrfToken } = await csrfTokenResponse.json();
const res = await fetch(sessionUrl, {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
data,
csrfToken,
}),
});
const json = await res.json();
return json;
}
also you should extend JWT callback in SvelteKitAuth options:
async jwt({
token,
user,
session,
trigger,
}: {
token: JWT,
user: SessionUser,
session?: Session,
trigger?: string | undefined,
}) {
if (trigger === 'signIn' && user) {
Object.assign(token, { user });
} else if (trigger === 'update') {
Object.assign(token, { user: session?.user });
}
return token;
},
it's should cover update
trigger.
Hello @stalkerg - thanks for your efforts. Updating session from client to server is still broken in SvleteKitAuth. The code you have provided won't work as the session-token cookie does not gets updated. You can refer this PR (and associated issues) for more detail understanding. Thanks.
@aakash14goplani it's working fine for me, I can update session user and it's keeps during browser restart.
cookie authjs.session-token
also updated correctly.
The PR #9497 is more about run getSession or set session from the SSR, if you will run my function from the client it will not a case. Also, with a small change it will work even on SSR.
The PR #9497 is more about run getSession or set session from the SSR, if you will run my function from the client it will not a case. Also, with a small change it will work even on SSR.
Which small change Yury? Can you please let me know?
@aakash14goplani this function can return also cookie
and after that you can proxy a such cookie to the browser.
return { json, cookie: res.headers.getSetCookie() };
something like this, after in your SSR function you can just set a such cookies.
After recent changes made in SvelteKitAuth v0.10 and v0.11, I am using following approach for to-and-fro communication.
Client to Server
General Idea: Update data by making API call
- Client initiates an API request and passes a query param (from ".svelte" files)
async function updateUserData() { await fetch(base + '/api/update-user-data?query=update-user-data', { method: 'POST' }); }
- We can create a dummy API route to handle this request. (routes/api/update-user-data)
export const POST = (() => { try { return new Response('User data updated', { status: 200 }); } catch (error: any) { return new Response('Error while updating user data: ' + error?.message, { status: 200 }); } }) satisfies RequestHandler;
- Since SvelteKitAuth is configured via hooks, it can intercept all the incoming network request. We can filter the one with query params within the "jwt" callback. Why "jwt" callback - because these callbacks are triggered multiple times and on every network requests.
async jwt({ token, account, profile }) { ... const shouldUpdateToken = args.event.url.searchParams.get('query') === 'update-user-data'; if (shouldUpdateToken) { console.log('token before update [library]: ', token?.library); token = { ...token, library: 'SvelteKitAuth: v0.11.1' // <- add items here, hardcode values or extract out of request payload }; console.log('token after update [library]: ', token?.library); } return token; }
- With the introduction of
auth()
, the newly added values will persist in cookies and hence server will be updated with latest data without user having to re-login (WHICH WAS NOT POSSIBLE for versions <v0.10)
Server to Client
General Idea: Update data using hydration
- In the root "layout.ts" file, we can return session object to client. Please note - I said "layout.ts" and not "layout.server.ts"
- The "layout.server.ts" will hydrated only once during initial page load and if user updates data mid-way, they would have to refresh the page or re-login to see updated data on client side. Hence idea is to stick with "layout.ts" file.
- In "layout.ts", there is a special hack that must be implemented (you can do this in layout.server.ts but I prefer layout.ts). The hack is to include "url" parameter and do some operation using url parameter. This is important as "url" parameter will force this load function to execute on every navigation change and thus we can return latest session information back to client!
- We can create a dummy end point that returns the session data (routes/api/get-user-data)
export const GET = (async ({ locals }) => { const session = await locals.auth(); return new Response(JSON.stringify(session), { status: 200 }); }) satisfies RequestHandler;
- Here is the complete code
export const load = (async ({ url, fetch, data }) => { let session; try { if (browser) { if (url.href.includes('xxx')) console.log(''); // <- very important const sessionObject = await fetch(url.origin + base + '/api/get-user-data'); session = await sessionObject.json(); } else { session = data.session; } } catch (ex: any) { console.log('Error fetching session data, defaulting to parent data'); session = data.session; } return { session }; }) satisfies LayoutLoad;
- Client can get latest data using
$page.data.session
This is the long route that I have to implement in SvelteKit as there are no helper methods exposed by SvelteKitAuth (when compared with NextAuth). So @balazsorban44 @ThangHuuVu and @ndom91 can you please go through this approach and let me know if this is correct way (for now) or something could be improved?
Example Repo: https://github.com/aakash14goplani/sveltekit-auth-session-token/tree/new-version-test
Having similar issue.
My authjs.session-token
cookie doesn't get updated while the session expire in DB gets updated when updateSession
happens. I'm using Email provider with Next.js app.
So extending session in DB won't make session extended since the cookie's expiration is not updated.
It's actually implemented here but for some reason my cookie doesn't get updated.
Ok. I found what was my issue. This explained . I was calling auth
on Next.js server component rendering and I expected it would update the cookie. But It doesn't as the comment explains. I should call the API separately from client component useEffect
or whatever. Now cookie gets updated correctly.
Ok. I found what was my issue. This explained . I was calling
auth
on Next.js server component rendering and I expected it would update the cookie. But It doesn't as the comment explains. I should call the API separately from client componentuseEffect
or whatever. Now cookie gets updated correctly.
@benevbright - Glad that you're able to find the solution but you posted in a wrong thread. This thread is meant for the "SvelteKit" framework and not "Next" framework!
@aakash14goplani you're right. I hid my comments as off topic. 🙏
comment by @stalkerg works for me. I am doing the complete registration when I need the user to fill the profile after registration.
here is my code.
export const actions: Actions = {
/**
*
* @param {RequestEvent} event sveltekit request event
* @returns
*/
complete: async (event: RequestEvent) => {
const form = await superValidate(event.request, zod(schema));
const session = (await event.locals.auth()) as QuantmAdapterSession;
if (!form.valid) {
return fail(400, { form });
}
const name = form.data.name;
const getopts = { method: 'GET' };
const postopts = { method: 'POST' };
const headers = { 'Content-Type': 'application/json' };
const refresh = async (team: Team) =>
event
// get authjs crsf token
.fetch(`${base}/auth/csrf`, { ...getopts })
.then(response => response.json())
// assign user to team
.then(csrf => {
// @ts-expect-error we know there will be user.
session.user.team_id = team.id;
return JSON.stringify({ data: session, ...csrf });
})
// update token with updated user
.then(body => event.fetch(`${base}/auth/session`, { ...postopts, headers, body }))
.then(response => response.json())
.then(() => ({ form, team }));
return api().auth.createTeam({ name }).then(refresh);
},
};
I haven't read through all of the posts in this thread, but there is an update
/ unstable_update
method that's returned from NextAuth()
in the Next.js side of things (live usage example here: https://next-auth-example.vercel.app/client-example).
So for implementing this in SvelteKit + Auth.js projects, maybe its helpful to look at that next-auth implementation. Yall can find it here: https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/lib/actions.ts#L110-L133
Found the solution (at least for now) https://blog.aakashgoplani.in/how-to-exchange-data-between-client-and-server-using-sveltekitauth