next-auth
next-auth copied to clipboard
Tokens rotation does not persist the new token
Environment
When rotating tokens, new token is not stored and thus not reused, so token is lost. The old token still persists instead and used for all further iterations of current session. Only initial token generated on login works and reused constantly.
I use Keycloak as the external IDP Keycloak — 21.1.1 Nextjs — 13.4.2 Next-auth — 4.22.1 Node — 16.2.0, 19.9.0
Reproduction URL
https://github.com/mrbodich/next-auth-example-fork.git
Describe the issue
When I use async jwt()
function in callbacks
section, I get the new token from external IDP successfully, create the new token object and return in async jwt()
just like documentation says.
Here is my piece of code in the last else
block (if access token is expired)
} else {
// If the access token has expired, try to refresh it
console.log(`Old token expired: ${token.expires_at}`)
const newToken = await refreshAccessToken(token)
console.log(`New token acquired: ${newToken.expires_at}`)
return newToken
}
Once token expired, and else
block is executed, I have constantly updating at each request. Here is what I get in the console logged:
Old token expired: 1684147058
Token was refreshed. New token expires in 60 sec at 1684147125, refresh token expires in 2592000 sec
New token acquired: 1684147125
Old token expired: 1684147058
Token was refreshed. New token expires in 60 sec at 1684147128, refresh token expires in 2592000 sec
New token acquired: 1684147128
Old token expired: 1684147058
Token was refreshed. New token expires in 60 sec at 1684147132, refresh token expires in 2592000 sec
New token acquired: 1684147132
As you see, 1684147058
is not changed between requests, so new JWT is just lost somewhere and not used for later requests. Though at the first login, returned jwt is used correctly.
How to reproduce
- Clone this repo https://github.com/mrbodich/next-auth-example-fork.git
- Transfer
.env.local.example
file to.env.local file
- When signing in, use credentials from
.env.local.example
file,row 13
- After sign-in, token will start refreshing after 1 minute (token lifespan set in Keycloak)
- Look in the console for next-auth logs
⚠️ Try to comment lines 18 ... 25
in the index.tsx
file (getServerSideProps function), and tokens will start rotating fine.
Expected behavior
Token returned in the async jwt()
function in callbacks
section must be used on the next request and not being lost.
similiar problem is stated in #6642 . Sadly it seems like noone cares about this issue atm, altough its a system breaking problem.
We cannot recreate the issue with the provided information. Please add a reproduction in order for us to be able to investigate.
Why was this issue marked with the incomplete
label?
To be able to investigate, we need access to a reproduction to identify what triggered the issue. We prefer a link to a public GitHub repository (template), but you can also use a tool like CodeSandbox or StackBlitz.
To make sure the issue is resolved as quickly as possible, please make sure that the reproduction is as minimal as possible. This means that you should remove unnecessary code, files, and dependencies that do not contribute to the issue.
Please test your reproduction against the latest version of NextAuth.js (next-auth@latest
) to make sure your issue has not already been fixed.
I added a link, why was it still marked?
Ensure the link is pointing to a codebase that is accessible (e.g. not a private repository). "example.com", "n/a", "will add later", etc. are not acceptable links -- we need to see a public codebase. See the above section for accepted links.
What happens if I don't provide a sufficient minimal reproduction?
Issues with the incomplete
label that receives no meaningful activity (e.g. new comments with a reproduction link) are closed after 7 days.
If your issue has not been resolved in that time and it has been closed/locked, please open a new issue with the required reproduction. (It's less likely that we check back on already closed issues.)
I did not open this issue, but it is relevant to me, what can I do to help?
Anyone experiencing the same issue is welcome to provide a minimal reproduction following the above steps. Furthermore, you can upvote the issue using the :+1: reaction on the topmost comment (please do not comment "I have the same issue" without repro steps). Then, we can sort issues by votes to prioritize.
I think my reproduction is good enough, why aren't you looking into it quicker?
We look into every NextAuth.js issue and constantly monitor open issues for new comments.
However, sometimes we might miss one or two. We apologize, and kindly ask you to refrain from tagging core maintainers, as that will usually not result in increased priority.
Upvoting issues to show your interest will help us prioritize and address them as quickly as possible. That said, every issue is important to us, and if an issue gets closed by accident, we encourage you to open a new one linking to the old issue and we will look into it.
Useful Resources
Hello @balazsorban44. I've deployed Keycloak, made necessary setup and pushed my example based on the latest example repo fork to github. https://github.com/mrbodich/next-auth-example-fork.git
Alternatively, I've updated the main question, section Reproduction URL and How to reproduce
You can find the credentials to login in the .env.local.example file, along with other necessary env variables, so just transfer this file to the .env.local file. I've set token lifespan to 1 minute, so it's the time you should wait before token will want to refresh
Updated issue description
I've found more details. Token rotation worked fine when using client-side session request. Once I've configured server-side session passing to props, refreshed tokens stopped persisting.
Look at this file in my repo. Tokens are rotating fine if you will comment lines 18 ... 25
index.tsx
//Token is not persisting when using server side session
export const getServerSideProps: GetServerSideProps = async (context) => {
const session = await getSession(context)
return {
props: {
session
}
}
}
similiar problem is stated in #6642 . Sadly it seems like noone cares about this issue atm, altough its a system breaking problem.
Yet not quite similar, since this issue refers to the older next-auth package, not the new @auth. Furthermore, that discussion relates to the database strategy, not JWT.
@Mikk36 I disagree. As the author of referred discussion the problem is described with JWT tokens and not database strategy and problem seems to remain also we newer versions (its true that i haven't tested with latest version).
similiar problem is stated in #6642 . Sadly it seems like noone cares about this issue atm, altough its a system breaking problem.
Yet not quite similar, since this issue refers to the older next-auth package, not the new @auth. Furthermore, that discussion relates to the database strategy, not JWT.
@Mikk36 I disagree. As the author of referred discussion the problem is described with JWT tokens and not database strategy and problem seems to remain also we newer versions (its true that i haven't tested with latest version).
My bad, I mixed up discussion numbers and thought it was something else.
@mrbodich Instead of using getSession
inside getServerSideProps
, please use getServerSession
as stated here. This persists the refresh token.
// index.tsx
import { authOptions } from '@/pages/api/auth/[...nextauth]'
export const getServerSideProps: GetServerSideProps = async (context) => {
const session = await getServerSession(context.req, context.res, authOptions)
return {
props: {
session
}
}
}
@mrbodich Instead of using
getSession
insidegetServerSideProps
, please usegetServerSession
as stated here. This persists the refresh token.// index.tsx import { authOptions } from '@/pages/api/auth/[...nextauth]' export const getServerSideProps: GetServerSideProps = async (context) => { const session = await getServerSession(context.req, context.res, authOptions) return { props: { session } } }
I have tried this and getting such error on backend
Error: Error serializing .session.user.image returned from getServerSideProps in "/".
Can you try my attached example project and make your changes in the index.tsx file please? It's already configured for the remote IDP so just rename .env.local.example
file to .env.local
, username and password just mentioned in this env file too.
@mrbodich That is not related to getServerSession
. You need to properly set the user
object inside jwt
callback to make sure that no key inside the user
object is undefined
. It either has to have a value or has to be null
.
Please check PR - https://github.com/mrbodich/next-auth-example-fork/pull/1 for detailed code.
@mrbodich That is not related to
getServerSession
. You need to properly set theuser
object insidejwt
callback to make sure that no key inside theuser
object isundefined
. It either has to have a value or has to benull
.Please check PR - mrbodich/next-auth-example-fork#1 for detailed code.
Thank you @anampartho, I got the idea now. Just was confused why it worked without server session handling.
Can I ask you to give me a very important tip that I can't understand please? How can I use getServerSession not only in the root '/' route (or on each route separately), but on the very top level so all routes will inherit that? And how is it possible to add extra properties on the lower levels or sub-routes?
Adding getServerSession
to _app.tsx
does not work.
@mrbodich That is not related to
getServerSession
. You need to properly set theuser
object insidejwt
callback to make sure that no key inside theuser
object isundefined
. It either has to have a value or has to benull
.Please check PR - mrbodich/next-auth-example-fork#1 for detailed code.
By the way, I came up with this solution. Updated provider's profile
method a bit with image: profile.picture ?? null
, just added the optional chaining for the image
property:
const keycloak = KeycloakProvider({
clientId: process.env.KEYCLOAK_ID,
clientSecret: process.env.KEYCLOAK_SECRET,
issuer: process.env.KEYCLOAK_ISSUER,
authorization: { params: { scope: "openid email profile offline_access" } },
tokenUrl: 'protocol/openid-connect/token',
profile(profile, tokens) {
return {
id: profile.sub,
name: profile.name ?? profile.preferred_username,
email: profile.email,
image: profile.picture ?? null,
}
},
});
PS: Thank you so much for your help.
@mrbodich That is not related to
getServerSession
. You need to properly set theuser
object insidejwt
callback to make sure that no key inside theuser
object isundefined
. It either has to have a value or has to benull
. Please check PR - mrbodich/next-auth-example-fork#1 for detailed code.Thank you @anampartho, I got the idea now. Just was confused why it worked without server session handling.
Can I ask you to give me a very important tip that I can't understand please? How can I use getServerSession not only in the root '/' route (or on each route separately), but on the very top level so all routes will inherit that? And how is it possible to add extra properties on the lower levels or sub-routes?
Adding
getServerSession
to_app.tsx
does not work.
@mrbodich Unfortunately, you have to use getServerSession
on each routes getServerSideProps
. There is no way to use it on /
and make the session available on all routes.
I am using getServerSession in the app directory, but the problem stil occurs. But I guess the problem is because of the following issue stated by this user: https://github.com/nextauthjs/next-auth/discussions/6642#discussioncomment-5942013
I am using getServerSession in the app directory, but the problem stil occurs. But I guess the problem is because of the following issue stated by this user: #6642 (comment)
@osmandvc Do you have a workaround for that?
I am using getServerSession in the app directory, but the problem stil occurs. But I guess the problem is because of the following issue stated by this user: #6642 (comment)
@osmandvc Do you have a workaround for that?
Sadly I did not find a really convenient way without too much overhead to make it work with RSC. The only solution currently seems like to switch to traditional client-side Authentication with useSession and a SessionProvider.
I am using getServerSession in the app directory, but the problem stil occurs. But I guess the problem is because of the following issue stated by this user: #6642 (comment)
@osmandvc Do you have a workaround for that?
Sadly I did not find a really convenient way without too much overhead to make it work with RSC. The only solution currently seems like to switch to traditional client-side Authentication with useSession and a SessionProvider.
Could you give me an example?
Any progress with this bug? I cannot implement token refreshing with next auth. After login i getting new set of tokens and first refreshing is ok but when i get new set the old tokens are not updated and next refresh call gives me error.
I also have this problem with @auth/core 0.8.2
and @auth/sveltekit 0.3.3
using a custom provider for Azure B2C.
Since sveltekit does prefetching when hovering links it triggers an awful lot of "token refreshes" after the first access token expires.
Any progress with this bug? I cannot implement token refreshing with next auth. After login i getting new set of tokens and first refreshing is ok but when i get new set the old tokens are not updated and next refresh call gives me error.
I use keycloack and I manage to make the token refresh a few seconds before it expires but the getsesion still gets the old token but when I refresh the browser tab with f5 it gets the refreshed token, I am trying to do something to observe this change, like a useeffect.
import NextAuth, { KeycloakTokenSet, NextAuthOptions } from "next-auth";
import { JWT } from "next-auth/jwt";
import KeycloakProvider from "next-auth/providers/keycloak";
const keycloak = KeycloakProvider({
clientId: process.env.KEYCLOAK_ID,
clientSecret: process.env.KEYCLOAK_SECRET,
issuer: process.env.KEYCLOAK_ISSUER,
authorization: { params: { scope: "openid email profile offline_access" } },
});
async function doFinalSignoutHandshake(token: JWT) {
if (token.provider == keycloak.id) {
try {
const issuerUrl = keycloak.options!.issuer!;
const logOutUrl = new URL(`${issuerUrl}/protocol/openid-connect/logout`);
logOutUrl.searchParams.set("id_token_hint", token.id_token);
const { status, statusText } = await fetch(logOutUrl);
console.log("Completed post-logout handshake", status, statusText);
} catch (e: any) {
console.error("Unable to perform post-logout handshake", e?.code || e);
}
}
}
function parseJwt(token: string) {
return JSON.parse(Buffer.from(token.split(".")[1], "base64").toString());
}
async function refreshAccessToken(token: JWT): Promise<JWT> {
try {
// We need the `token_endpoint`.
const response = await fetch(
`${keycloak.options!.issuer}/protocol/openid-connect/token`,
{
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
client_id: keycloak.options!.clientId,
client_secret: keycloak.options!.clientSecret,
grant_type: "refresh_token",
refresh_token: token.refresh_token,
}),
method: "POST",
}
);
const tokensRaw = await response.json();
const tokens: KeycloakTokenSet = tokensRaw;
// console.log(tokensRaw)
if (!response.ok) throw tokens;
const expiresAt = Math.floor(Date.now() / 1000 + tokens.expires_in);
console.log(
`Token was refreshed. New token expires in ${tokens.expires_in} sec at ${expiresAt}, refresh token expires in ${tokens.refresh_expires_in} sec`
);
const newToken: JWT = {
...token,
access_token: tokens.access_token,
refresh_token: tokens.refresh_token,
id_token: tokens.id_token,
expires_at: expiresAt,
provider: keycloak.id,
};
return newToken;
} catch (error) {
// console.error("Error refreshing access token: ", error)
console.error("Error refreshing access token: ");
throw error;
}
}
// For more information on each option (and a full list of options) go to https://next-auth.js.org/configuration/options
export const authOptions: NextAuthOptions = {
secret: process.env.NEXTAUTH_SECRET,
// https://next-auth.js.org/configuration/providers/oauth
providers: [keycloak],
theme: {
colorScheme: "light",
},
pages: {
signIn: "/login",
signOut: "/login",
},
callbacks: {
async jwt({ token, account, user }) {
console.log("Executing jwt()");
if (account && user) {
const jwtDecoded = parseJwt(account.access_token as string);
if (!account.access_token)
throw Error("Auth Provider missing access token");
if (!account.refresh_token)
throw Error("Auth Provider missing refresh token");
if (!account.id_token) throw Error("Auth Provider missing ID token");
// Save the access token and refresh token in the JWT on the initial login
const newToken: JWT = {
...token,
access_token: account.access_token,
refresh_token: account.refresh_token,
id_token: account.id_token,
expires_at: Math.floor(account.expires_at ?? 0),
provider: account.provider,
userName: jwtDecoded.preferred_username,
userRoles: jwtDecoded.resource_access.account.roles,
};
return newToken;
}
const timeRemaining = token.expires_at * 1000 - Date.now();
if (timeRemaining > 30000) {
// If the token's remaining time is greater than 30 seconds, return the current token
console.log(
`\n>>> ${timeRemaining / 1000} seconds left until token expires`
);
return token;
}
console.log(`\n>>> Old token expired`);
// If the access token has expired or will expire within 30 seconds, try to refresh it
const newToken = await refreshAccessToken(token);
console.log(`New token adquired: ${newToken.expires_at}`);
return token;
},
async session({ session, token }) {
console.log(`Executing session() with token ${token.expires_at}`);
// You need to set the user object properly in jwt callback,
// Error: Error serializing .session.user.image was occuring because
// session.user.image was undefined, it needs to be value || null
session.user = { ...token };
return { ...session };
},
},
events: {
signOut: async ({ session, token }) => doFinalSignoutHandshake(token),
},
jwt: {
// maxAge: 60, // 20 horas
maxAge: 32400, // 9h
},
session: {
// maxAge: 30 * 24 * 60 * 60, // 30 days : 2592000, same as in Keycloak
maxAge: 32400, // 9h
},
};
export default NextAuth(authOptions);
API:
import { NODE_ENV, uri } from "@/constants/environment-variables";
import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
const axiosInstance = axios.create({
baseURL: uri[NODE_ENV],
});
async function getData() {
const res = await axios.get("/api/auth/session");
return res;
}
const setAuthorizationHeader = async (axiosInstance: AxiosInstance) => {
// You can try to get the access token that way
const session = await getData();
// Or you can try to use Next Auth function
// const session = getSession()
// if I'm on the /api/auth/session page and I press f5 to refresh the tab, the new access token is there,
// however, neither getData() nor getSession() remains the previous access token, they will only update,
// if I refresh the page. I believe that useeffect solves it but I still don't know how to do it
console.log(session);
if (session) {
const token = session.data.user.access_token;
console.log(session);
axiosInstance.interceptors.request.use((config) => {
config.headers.Authorization = `Bearer ${token}`;
return config;
});
}
};
setAuthorizationHeader(axiosInstance);
const api = (axios: AxiosInstance) => {
return {
get: function <T>(url: string, config: AxiosRequestConfig = {}) {
return axios.get<T>(url, config);
},
put: function <T>(
url: string,
body: unknown,
config: AxiosRequestConfig = {}
) {
return axios.put<T>(url, body, config);
},
post: function <T>(
url: string,
body: unknown,
config: AxiosRequestConfig = {}
) {
return axios.post<T>(url, body, config);
},
delete: function <T>(url: string, config: AxiosRequestConfig = {}) {
return axios.delete<T>(url, config);
},
};
};
export default api(axiosInstance);
I am using getServerSession in the app directory, but the problem stil occurs. But I guess the problem is because of the following issue stated by this user: #6642 (comment)
@osmandvc Do you have a workaround for that?
Sadly I did not find a really convenient way without too much overhead to make it work with RSC. The only solution currently seems like to switch to traditional client-side Authentication with useSession and a SessionProvider.
Could you give me an example?
I did it similar to this comment: https://github.com/nextauthjs/next-auth/issues/5647#issuecomment-1291898265 . Basically follow the Tutorial on the Nextauth-Page, the only thing that changes is where you put your SessionProvider. Make a seperate Client-Component put your Provider there, and wrap your Content in your Root-Layout with the newly created Client-Component. This way you make sure that the layout.tsx remains a Server-Component. Now every underlying page has Access to the Session-Object (mostly with delay, because client-side)
As of [email protected] (experimental), this is still an occurring issue. The initial token is stored; however, going forward, updates made to it are never saved (at least not to the token that is provided within jwt
callback). As such, auth.js will attempt to refresh the token since it's always checking against the first expires_at
timestamp.
Due to this issue, refresh token rotation is in practice, not possible with auth.js :(
Edit 1:
It's pretty evident that the next-auth.session-token
cookie is not updated whenever it has been created initially, despite returning new a new object within the jwt
callback. The difference seems to be that the first time around, the flow is triggered within the core/callback.js
whereas subsequent requests are handled by the core/session.js
file.
Edit 2:
After some further investigation, the first request works as intended since auth.js is in control of the redirection flow, ie. when you go through an OAuth login procedure. At this point they are able to update the next-auth.session-token
cookie and reflect the changes made within the jwt
callback.
However, when using something like getServerSession(...)
method that invokes the jwt
callback, the new cookie is in actuality added to the response as intended. However, next is controlling the request, and strips the set-cookie header since it's being run from a normal page and not an API, this notion is further supported in #7522.
As such, there is much that can be done currently, until Next makes it possible to set cookies sitewide.
Edit 3: This is likewise an issue within Sveltekit (#6447) and, unfortunately, not isolated to Next. The fact that auth.js doesn't work with both of these frameworks indicates (to me) that the refresh rotation functionality is currently broken. I think it would at least make sense to make this very apparent on the guide page that their example currently is not functional.
Hi @balazsorban44 , refresh token rotation seems to be broken. Do you know if there is a plan to fix this issue? Is someone looking into it?
Have a look at the issue here: https://github.com/nextauthjs/next-auth/discussions/4229. Managed to get it work using update() from the useSession hook.
It seems, that token rotation actually theoretically works when using auth.js In all my cases auth.js returns a valid 'set-cookie' header as a response.
When using token rotation after a login I noticed, that after a redirect it calls Auth again on /api/auth/session.
I am using qwik-auth so I can only compare to that. It seems, that qwik-auth makes an exception when manually calling /api/auth/session and does not set the 'set-cookie' after a successful response.
Following I will describe what happens in qwik-auth, but I assume that the issue is similar with other implementations.
The issue is, that the updated cookie is not received by the client, since qwik-auth works like this:
Context: Reference: https://github.com/BuilderIO/qwik/blob/47c2d1e838e9f748b191e983dabb0bac476f8083/packages/qwik-auth/src/index.ts#L18
const actions: AuthAction[] = [
'providers',
'session',
'csrf',
'signin',
'signout',
'callback',
'verify-request',
'error',
];
onRequest: Reference: https://github.com/BuilderIO/qwik/blob/47c2d1e838e9f748b191e983dabb0bac476f8083/packages/qwik-auth/src/index.ts#L90
const onRequest = async (req: RequestEvent) => {
if (isServer) {
const prefix: string = '/api/auth';
const action = req.url.pathname.slice(prefix.length + 1).split('/')[0] as AuthAction;
const auth = await authOptions(req);
// We notice, that there is no action that is named like so and neither is the prefix present.
if (actions.includes(action) && req.url.pathname.startsWith(prefix + '/')) {
const res = await Auth(req.request, auth);
const cookie = res.headers.get('set-cookie');
if (cookie) {
req.headers.set('set-cookie', cookie);
res.headers.delete('set-cookie');
fixCookies(req);
}
throw req.send(res);
} else {
// So this gets triggered and getSessionData(...) does not set the cookies on response.
req.sharedMap.set('session', await getSessionData(req.request, auth));
}
}
};
The fix I did is here: https://github.com/BuilderIO/qwik/pull/4960/files
Ok so I took some time just looking at the react code, but since I don't use react I cannot confirm.
Hopefully somebody can confirm this for me:
1. Get Session gets called:
- broadcast.post triggers
fetchData
https://github.com/nextauthjs/next-auth/blob/99035b98f9e81ed54aa8f79482b2f45509f9adf6/packages/next-auth/src/react/index.tsx#L161-L172
2. fetchData gets called:
- fetchData only returns
res.json()
https://github.com/nextauthjs/next-auth/blob/99035b98f9e81ed54aa8f79482b2f45509f9adf6/packages/next-auth/src/client/_utils.ts#L32-L60
If fetchData
works like in qwik (Reference) then we must return cookies as well, since /api/session/
returns Set-Cookie
Headers when using jwt rotation.
Probably triggered here: https://github.com/nextauthjs/next-auth/blob/99035b98f9e81ed54aa8f79482b2f45509f9adf6/packages/next-auth/src/core/routes/session.ts#L90
So what's needed is a way to have the cookie being pushed on any request, not just /session
Sounds like you can just use a middleware. On every request initiated in protected routes, fetch to /api/session
and set the cookies in the middleware response.
@rinvii ofc that would work, but then you also have to call signin manually since after signin the jwt rotation gets triggered again since session is called and at that point you are building your own implementation of /next-auth/src/react/index.ts
I am assuming that react/index.ts is the same like https://github.com/BuilderIO/qwik/blob/47c2d1e838e9f748b191e983dabb0bac476f8083/packages/qwik-auth/src/index.ts
// Disclaimer: The pull request for qwik got merged, so I assume that it is correct there. I do not know react. This is all just guess work based on looking at similar files.
@aliyss I don't think calling sign in is necessary in order to solve the token rotation issue. While you can and should call sign in when you are authenticating, subsequent authorizations do not need to call sign in. I don't know if I understood the point you were making.
Is there anything preventing you from configuring the session callback to return new access tokens when needed? Note the source code describes the jwt callback as follows:
/**
* This callback is called whenever a JSON Web Token is created (i.e. at sign in)
* or updated (i.e whenever a session is accessed in the client).
* Its content is forwarded to the `session` callback,
* where you can control what should be returned to the client.
* Anything else will be kept from your front-end.
*
* The JWT is encrypted by default.
*
* [Documentation](https://next-auth.js.org/configuration/callbacks#jwt-callback) |
* [`session` callback](https://next-auth.js.org/configuration/callbacks#session-callback)
*/
Therefore, forward access tokens to the session callback. In order to rotate the tokens, you have to update the cookies. You can do this by forwarding the response from the session callback as new cookies. This can be done through middleware.
Note: reading through past comments, there seems to be a fundamental misunderstanding on how getServerSession works. This function cannot update the session; it can only validate the session. If you're wanting to update the session, do it through the client (as @walshhub did) or middleware.
Middleware example: https://github.com/nextauthjs/next-auth/issues/8254#issuecomment-1690474377
I don't know if I understood the point you were making.
I think we are talking past each other on some topics, so I will kind of clarify it.
reading through past comments, there seems to be a fundamental misunderstanding on how getServerSession works. This function cannot update the session; it can only validate the session.
I can't make a comment on that, but I'm talking about getSession in /packages/next-auth/src/react/index.tsx
. Not getServerSession in /packages/next-auth/src/react/index.tsx
.
Although looking at that it seems the cookies are updated as well: https://github.com/nextauthjs/next-auth/blob/9c6f81308c1db55192744e68a3372131a690d15c/packages/next-auth/src/next/index.ts#L220-L222
In the case of the example for the token rotation described here: https://authjs.dev/guides/basics/refresh-token-rotation#jwt-strategy
This function gets called here: https://github.com/nextauthjs/next-auth/blob/99035b98f9e81ed54aa8f79482b2f45509f9adf6/packages/next-auth/src/core/routes/session.ts#L51
Then the refresh-token is triggered. But on reloading the page the cookies have not updated. So when it is called again it still uses the old token.