next-auth icon indicating copy to clipboard operation
next-auth copied to clipboard

Clarification needed on `unstable_update` and required adapter methods

Open adriangalilea opened this issue 1 year ago • 1 comments

What is the improvement or update you wish to see?

I'm trying to use the unstable_update method. And despite appearing on the documentation, it has 0 information on how it's used.

Is there any context that might help us understand?

I tried:

const newSession = await unstable_update({
        user: { username: newUsername },
      });

But it does not update the session.

Since I'm using a custom adapter I thought that the updateSession may be relevant, but after creating it and adding console.log's to it I saw nothing printed on the console, so the unstable_update is not invoking updateSession nor is working by default.

So I'm wondering how is that meant to work.

Does the docs page already exist? Please link to it.

https://authjs.dev/reference/nextjs#unstable_update

adriangalilea avatar Aug 28 '24 10:08 adriangalilea

UPDATE: I managed to make it work:

jwt: async ({ token, user, trigger, session }) => {
      if (user) {
        if (!user.id) {
          throw new Error("User ID is required");
        }
        token.user = {
          id: user.id,
          username: user.username,
        };
      }
      if (trigger === "update") {
        token.user = {
          ...token.user,
          username: session.user.username,
        };
      }
      return token;
    },

But I'm guessing it should work without needing this, not sure how.

adriangalilea avatar Aug 28 '24 11:08 adriangalilea

UPDATE: I managed to make it work:

jwt: async ({ token, user, trigger, session }) => {
      if (user) {
        if (!user.id) {
          throw new Error("User ID is required");
        }
        token.user = {
          id: user.id,
          username: user.username,
        };
      }
      if (trigger === "update") {
        token.user = {
          ...token.user,
          username: session.user.username,
        };
      }
      return token;
    },

But I'm guessing it should work without needing this, not sure how.

This worked for me. I was able to call unstable_update from a server action and the updated session persists on refresh. Thank you!

alxedelweiss avatar Sep 24 '24 04:09 alxedelweiss

The above is the correct solution for now. It's unatable/undocumented because the current DX isn't desirable and would like to polish it more first

balazsorban44 avatar Sep 25 '24 22:09 balazsorban44

UPDATE: I managed to make it work:

jwt: async ({ token, user, trigger, session }) => {
      if (user) {
        if (!user.id) {
          throw new Error("User ID is required");
        }
        token.user = {
          id: user.id,
          username: user.username,
        };
      }
      if (trigger === "update") {
        token.user = {
          ...token.user,
          username: session.user.username,
        };
      }
      return token;
    },

But I'm guessing it should work without needing this, not sure how.

This worked for me. I was able to call unstable_update from a server action and the updated session persists on refresh. Thank you!

so how would you pass the session.user.username is it via update ? from useSession()

I am using this in a server action

unstable_update({
      user: {
        email: updatedUser.email,
        name: updatedUser.name,
      },
    });

inside of JWT callback

if (trigger === "update" && session) {
        const updatedUser = await getUserById(token.sub);
        if (updatedUser) {
          token.email = updatedUser.email;
          token.name = updatedUser.name;
          token.role = updatedUser.role;
          token.isTwoFactorEnabled = updatedUser.isTwoFactorEnabled;
          token.lastUpdate = Date.now();
        }
}

EDIT: using update with trigger updates the client-side session but failed to update server-side session data for that I have used unstable_update()

pantharshit007 avatar Nov 09 '24 11:11 pantharshit007

any update please?

sovetski avatar Jan 02 '25 08:01 sovetski

You can use unstable_update for the server side. It works, but you need some more steps.

  1. Make sure you receive an update in callbacks jwt:
     if (trigger === 'update' && session?.user) {
           token.user = session?.user;
           return token;
     }
  1. By default, unstable_update only receive User model, you can declare new model when using this:
    declare module 'next-auth' {
        interface Session {
          user: {
            id?: string;
            email?: string | null;
            userName?: string | null;
            fullName?: string | null;
            accessToken?: string | null;
            refreshToken?: string | null;
          } & DefaultSession['user'];
        }
     }

After completing all the steps above, you still won’t see any changes on the client side because it is only for the server. You need to call something to trigger a session update after using unstable_update.

For me, I'm using NextAuth v5. After running unstable_update on the server side, I call await getSession() to trigger the session update on the client side. By default, await getSession() already triggers a session update internally.

Docs: https://next-auth.js.org/getting-started/client#getsession

wahinguyen avatar Apr 03 '25 10:04 wahinguyen

Using unstable_update in NextAuth Beta

You can use the unstable_update helper from NextAuth to update the session server-side.

1. Import unstable_update

export const { handlers, signIn, signOut, auth, unstable_update } = NextAuth({
  // your config
});

2. Create a server function to update the session

Inside a "use server" file, define a helper like this:

"use server";

import { auth, unstable_update } from "@/auth"; // adjust path accordingly
import type { Session } from "next-auth";

export const updateSession = async ({
  data,
}: {
  data: Partial<Session["user"]>;
}) => {
  const session = await auth();
  if (session) {
    await unstable_update({
      ...session,
      user: {
        ...session.user,
        ...data,
      },
    });
  }
};

3. Call updateSession wherever you need to refresh/update session data

For example, after refreshing access tokens:

const newToken = await arvasitAuth.refreshAccessToken({
  token: refreshToken,
});

if (newToken.accessToken) {
  setCookie("accessToken", newToken.accessToken);
  setCookie("refreshToken", newToken.refreshToken);

  await updateSession({
    data: {
      accessToken: newToken.accessToken,
      refreshToken: newToken.refreshToken,
    },
  });
}

PAAPII10 avatar Jun 12 '25 06:06 PAAPII10