N
Nuxt3mo ago
Hituro

nuxt-auth-utils not updating user on client

Hi, almost complete Nuxt noob here I'm working on a Nuxt app with nuxt-auth-utils. Our authentication tokens have a short validity period during which they don't need to be revalidated, so as part of the user session created with setUserSession() we store the expiry time, and then on the client check that time to see if we need to revalidate. The problem is that, once we do revalidate, the user session on the client is never updated. The revalidation on the server looks like this:
export default defineEventHandler(async (event) => {
const { user } = await requireUserSession(event);
const authcheck = await api.base.authcheck([user.jwt]);

await setUserSession(event, {
user: authcheck.result,
});

return authcheck;
});
export default defineEventHandler(async (event) => {
const { user } = await requireUserSession(event);
const authcheck = await api.base.authcheck([user.jwt]);

await setUserSession(event, {
user: authcheck.result,
});

return authcheck;
});
I can see from the server logs that this gets the correct expiry time from the API, and if I do useUserSession().fetch() on the client I can see the updated value, but the user returned by useUserSession() never updates, no matter what I do.
export default defineNuxtRouteMiddleware(async (to) => {
const event = useRequestEvent();
const { user, clear: clearSession } = useUserSession();

const { jwt, expiry } = user.value ?? {};
// expiry is ALWAYS the old value
export default defineNuxtRouteMiddleware(async (to) => {
const event = useRequestEvent();
const { user, clear: clearSession } = useUserSession();

const { jwt, expiry } = user.value ?? {};
// expiry is ALWAYS the old value
I've tried setUserSession(), replaceUserSession(), and even createUserSession(), and I can see that the request updates the Nuxt cookie, but the value in useUserSession() is only ever the first one that was set when the user logs in. I'm totally stuck, so any insight is greatfully recieved!
33 Replies
kapa.ai
kapa.ai3mo ago
Hi @Hituro. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- You're encountering a common issue in Nuxt apps using session cookies: after updating the session on the server (with setUserSession), the client-side session (as returned by useUserSession) does not immediately reflect the new value—unless you manually call useUserSession().fetch(). This is expected behavior with nuxt-auth-utils, because the session data is stored in a sealed cookie. When you update the session on the server, the new cookie is sent in the response. However, the client-side composable (useUserSession) will only see the updated value after a page reload or after you explicitly call fetch() to re-read the session from the cookie. This is because the composable does not automatically re-fetch the session data after an API call that updates the cookie; it will continue to use the value it had in memory until you tell it to refresh. From the official Nuxt recipe:
The module exposes a Vue composable to know if a user is authenticated in our application:
const { loggedIn, session, user, clear, fetch } = useUserSession()
const { loggedIn, session, user, clear, fetch } = useUserSession()
...after a successful login, you should call fetch() to refresh the session on the client side and get the updated user data.
await refreshSession()
await refreshSession()
Sessions and Authentication
So, after your revalidation API call, you must call useUserSession().fetch() (or the fetch/refreshSession method you destructure from useUserSession) to update the client-side session data. This is the only way for the client to see the new session value without a full page reload. Summary of what to do: - After your API call that updates the session, call useUserSession().fetch() on the client to refresh the session data. - The value in useUserSession().user will then reflect the updated session. If you already do this and still see stale data, ensure that your API call is actually setting the cookie and that the client is not caching the old value. Also, be aware that useUserSession is not reactive to cookie changes unless fetch() is called. If you need the session to update automatically after every relevant API call, you could wrap your API calls in a helper that always calls fetch() after a successful response. The knowledge sources do not mention any automatic reactivity for useUserSession to cookie changes, so manual refresh is required. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@335518469107744768> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Quantix
Quantix3mo ago
instead of passing your own custom expiry would it not be better to specify the maxAge on the user session? https://github.com/atinux/nuxt-auth-utils?tab=readme-ov-file#configuration
Hituro
HituroOP3mo ago
Quite possibly. I've just taken over this project from an AWOL dev, so I can't really ask the original reasoning
Quantix
Quantix3mo ago
if you call fetch() it updates the user session it doesn't automatically update
Hituro
HituroOP3mo ago
I could have sworn I tried that already
Quantix
Quantix3mo ago
🤔
Hituro
HituroOP3mo ago
And saw the updated result in the fetch, but the next call to useUserSession() showed the old value. But then I've tried a lot of things in a rather scattershot way, so I'll try it again
Quantix
Quantix3mo ago
the strange thing is calling fetch() from useUserSession in the code updates the session value which you'd expect to work code i'm referencing: https://github.com/atinux/nuxt-auth-utils/blob/main/src/runtime/app/composables/session.ts#L29 i'm wondering if it's maybe a reactivity issue
Hituro
HituroOP3mo ago
I've just put the fetch back in, and when I go to the next route it still reports the old value
Quantix
Quantix3mo ago
could you show the code?
Hituro
HituroOP3mo ago
One sec
export default defineNuxtRouteMiddleware(async (to) => {
const { user, clear: clearSession, fetch: fetchSession} = useUserSession();

const { jwt, expiry } = user.value ?? {};

if (jwt) {

// use the expiry string (in ISO date format) to check if we need to do the authentication check

console.log("Next authentication check at: "+expiry);

if (true || !expiry || new Date(expiry) < new Date()) {
console.log("Requesting authcheck");
const auth = await activateClient.base.authcheck();
const fetch = await fetchSession();

// rest of function removed
}
export default defineNuxtRouteMiddleware(async (to) => {
const { user, clear: clearSession, fetch: fetchSession} = useUserSession();

const { jwt, expiry } = user.value ?? {};

if (jwt) {

// use the expiry string (in ISO date format) to check if we need to do the authentication check

console.log("Next authentication check at: "+expiry);

if (true || !expiry || new Date(expiry) < new Date()) {
console.log("Requesting authcheck");
const auth = await activateClient.base.authcheck();
const fetch = await fetchSession();

// rest of function removed
}
Quantix
Quantix3mo ago
ok so when you say it's not updating are yo uchecking expiry after you call fetchSession() ?
Hituro
HituroOP3mo ago
Yes, on the next route change the middleware runs again and shows the old value Let me add some other value to it though, because I'm waiting 5 min between each change on the back end I'll add some random number to the user
Quantix
Quantix3mo ago
can you see the fetch request in the browser with the correct values
Hituro
HituroOP3mo ago
Yes Ah ha! It's changing the random value 😄 In 2 minutes I'll be able to see if it correctly changes the expiry date as well It works!
Quantix
Quantix3mo ago
huh what changed?
Hituro
HituroOP3mo ago
I am not sure what I changed this time, but apparently it did it
Quantix
Quantix3mo ago
lol
Hituro
HituroOP3mo ago
That's a good question. I think last time I used fetch, I was checking the values immediately and they were not updated, of course. Also I was using replaceUserSession instead of setUserSession Now I have to work out why the jwt is even in the user session I think it shouldn't be Because it can get pretty big, and might push the cookie limits
Quantix
Quantix3mo ago
if you were checking them immediately in the same middleware after fetch the expiry wouldn't be updated since you destructured it from a reactive value it's no longer reactive
Hituro
HituroOP3mo ago
I'm wondering if that's exactly what I did in the middle of my confused floundering, and didn't realise I wasn't even checking the right value
Quantix
Quantix3mo ago
at least it's all sorted now
Hituro
HituroOP3mo ago
I should be able to use loggedIn from useUserSession() rather than having to check the jwt value, right?
Quantix
Quantix3mo ago
ye
Hituro
HituroOP3mo ago
Ahh wait, I see that the server side code uses user.jwt throughout Of course, the server doesn't have persistant state either, I'm being dumb
Quantix
Quantix3mo ago
if it's just the server using it you could put it in the secure part of the session so it's not exposed to the client
Hituro
HituroOP3mo ago
That makes sense. I suspect the original dev just stuffed the whole response into user because that's easy, no destructuring at all I'll see if I can make that work 😄 The login route is just:
const result = await api.base.login(params);

if (!result.error) {
await setUserSession(event, {
user: result.result,
});
return {};
}
const result = await api.base.login(params);

if (!result.error) {
await setUserSession(event, {
user: result.result,
});
return {};
}
I guess I would be better destructuring the result and putting part in user and part in server
Quantix
Quantix3mo ago
depending on what's actually in there yeah it'd probably be better to be explicit so if the api changes for whatever reason you don't end up accidentally exposing something
Hituro
HituroOP3mo ago
Well this didn't work:
await setUserSession(event, {
user: result.result,
server: {
jwt: result.result.jwt
}
});
await setUserSession(event, {
user: result.result,
server: {
jwt: result.result.jwt
}
});
Not sure why. No errors, but it just doesn't log in Is that because the value is wrapped in a proxy? Oh yes, result.result.jwt.value doesn't make it isntantly fall over 😄
Quantix
Quantix3mo ago
the user session object actually has special properties on it user, secure and anything else you put in there https://github.com/atinux/nuxt-auth-utils?tab=readme-ov-file#session-management if the jwt is only used in nitro (server/) side you can put it in secure which means it doesn't get sent to the client at all
Hituro
HituroOP3mo ago
Ahh, I misnamed it secure not server 😄
Quantix
Quantix3mo ago
i actually thought it said server too

Did you find this page helpful?