-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Does Next Auth handle automatic JWT refresh? #5652
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
No, but there is a tutorial which explains how to implement it: https://next-auth.js.org/tutorials/refresh-token-rotation |
This comment was marked as spam.
This comment was marked as spam.
I don't believe this library supports out-of-the-box token refresh. I believe refetchInterval lets you set how often your client reaches out to Next Auth backend. It is still up to you to handle token refresh logic on the Next Auth backend. I had to implement refresh tokens myself for Google identity platform and it has weird issues. It doesn't help that Google tokens in particular are so short lived and expire quickly (probably intentional design by Google). In my opinion, authentication is not a solved problem (and is a damn nightmare) until refresh tokens fully seamlessly work. Would love to see that in a future version of this library π Zero disrespect intended. Thank you to the maintainers of this awesome library. |
We don't have out-of-the-box support yet, but we absolutely want to do this π Things are a bit hectic at the moment for the core team, so PR is welcome! |
You've got a PR that is almost working from what I've seen, can you not spare a few mates from the team to actually implement it? I'm not hating just asking :) |
I def agree that JWT auth is not complete without a token refresh strategy. Would love to see some movement on this. |
This would be amazing to have. Thank you for the amazing work! |
Hey guys, |
I also followed https://next-auth.js.org/tutorials/refresh-token-rotation to implement token refreshing However, for some reason the re-fetch interval doesn't seem to line up with my token's expiration time, (my guess is that the token is acquired from some cache or cookies so it might already be close to expiration). As a result there is a small window between token expiration and refetching where the token is invalid for making API calls. In addition, I assumed that the JWT callback function would fire every time https://next-auth.js.org/configuration/callbacks#jwt-callback
However, I found this issue #4227 which indicates that this is not the case:
I'm trying to find a way to modify the useSession hook so that if the token is expired, it automatically requests a new one but I'm not sure how to override it nor do I know how to manually trigger a repoll of the token. At this point I'm thinking of making the refetchInterval to a short timespan and making the token lifespan longer, but that seems like a very wasteful workaround. |
It looks like this issue did not receive any activity for 60 days. It will be closed in 7 days if no further activity occurs. If you think your issue is still relevant, commenting will keep it open. Thanks! |
no activity doesn't mean it's not needed |
It looks like this issue did not receive any activity for 60 days. It will be closed in 7 days if no further activity occurs. If you think your issue is still relevant, commenting will keep it open. Thanks! |
no activity doesn't mean it's not needed |
It looks like this issue did not receive any activity for 60 days. It will be closed in 7 days if no further activity occurs. If you think your issue is still relevant, commenting will keep it open. Thanks! |
no activity doesn't mean it's not needed |
I have been trying to remove the expired tokens, according to this topic, but it is not re-populated. What I am trying to do now is checking it manually, changing the session callback. First I verify if the import NextAuth from "next-auth";
import DiscordProvider from "next-auth/providers/discord";
import clientPromise from "@lib/mongodb";
import { MongoDBAdapter } from "@next-auth/mongodb-adapter";
import { ObjectId } from "mongodb";
export const authOptions = {
providers: [
DiscordProvider({
clientId: process.env.DISCORD_CLIENT_ID,
clientSecret: process.env.DISCORD_CLIENT_SECRET,
authorization: { params: { scope: "identify email guilds" } },
}),
],
adapter: MongoDBAdapter(clientPromise),
secret: process.env.NEXTAUTH_SECRET,
callbacks: {
session: async ({ session, user }) => {
const client = await clientPromise;
const db = client.db();
const { expires_at, refresh_token } = await db
.collection("accounts")
.findOne({
userId: new ObjectId(user.id),
});
// if (expires_at > Date.now()) {
if (expires_at < Date.now()) {
try {
const response = await fetch(
"https://discord.com/api/v10/oauth2/token",
{
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
client_id: process.env.DISCORD_CLIENT_ID,
client_secret: process.env.DISCORD_CLIENT_SECRET,
grant_type: "authorization_code",
refresh_token: refresh_token,
redirect_uri: "http://localhost:3000/",
scope: "identify email guilds",
}),
}
);
const data = await response.json();
console.log(data);
if (!response.ok) throw data;
await db.collection("accounts").updateOne(
{ userId: new ObjectId(user.id) },
{
$set: {
access_token: data.access_token,
expires_at: Date.now() + data.expires_in * 1000,
refresh_token: data.refresh_token || user.refreshToken,
},
},
{ upsert: false }
);
} catch (error) {
console.error("Error refreshing access token", error);
}
}
session.user.id = user.id;
return session;
},
},
};
export default NextAuth(authOptions); |
I'm eager to see built-in support for refresh tokens too! Here's the relevant refresh token-related snippets for how I set it up with the Keycloak provider. Helpersconst TOKEN_REFRESH_THRESHOLD_IN_SECONDS = 30;
const keycloakIssuer = process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER;
/**
* Takes a token, and returns a new token with updated
* `accessToken` and `accessTokenExpires`. If an error occurs,
* returns the old token and an error property
*/
async function refreshAccessToken({refresh_token}: {refresh_token: string}) {
const params = new URLSearchParams({
client_id: process.env.KEYCLOAK_CLIENT_ID as string,
grant_type: 'refresh_token',
refresh_token,
});
const url = `${keycloakIssuer}/protocol/openid-connect/token`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: params,
});
const refreshedTokens: TokenSet = await response.json();
if (!response.ok) {
throw refreshedTokens;
}
return refreshedTokens;
}
// ...
/**
* Checks if the access token is expired or not. Goal is try to refresh the token before it expires.
*
* Simplified version of this function:
* https://github.com/keycloak/keycloak/blob/67c64c37df4c33a90f4db1873620da06fb751cd4/js/libs/keycloak-js/src/keycloak.js#L575
*
* @param minValidityInSeconds The minimum validity time in seconds for the access token.
* @returns A boolean indicating whether the access token is expired or not.
*/
const isTokenExpired = (minValidityInSeconds: number, expiresAt: number) => {
return Date.now() + minValidityInSeconds * 1000 > expiresAt * 1000;
}; Auth OptionsSession Callback{
// other options
async session({ session, user }) {
/* Retrieve "userAccount" from adapter */
if (
userAccount &&
isTokenExpired(TOKEN_REFRESH_THRESHOLD_IN_SECONDS, userAccount.expires_at!)
) {
try {
const refreshedTokens = await refreshAccessToken({
refresh_token: userAccount?.refresh_token as string,
});
await myAdapter.linkAccount({
...userAccount,
access_token: refreshedTokens.access_token,
expires_at: Math.floor(
Date.now() / 1000 + (refreshedTokens.expires_in as number),
),
refresh_token:
refreshedTokens.refresh_token ?? userAccount.refresh_token,
});
session.accessToken = refreshedTokens.access_token as string;
session.idToken = refreshedTokens.id_token as string;
} catch (error) {
// The error property will be used client-side to handle the refresh token error
session.error = 'RefreshAccessTokenError';
return session;
}
} else {
session.accessToken = userAccount?.access_token as string;
session.idToken = userAccount?.id_token as string;
}
/* Assign attributes to session then return it for the frontend */
return session;
}
} |
@KalleV just 30 seconds of threshold is enough? I have no idea :) |
It looks like this issue did not receive any activity for 60 days. It will be closed in 7 days if no further activity occurs. If you think your issue is still relevant, commenting will keep it open. Thanks! |
"no activity doesn't mean it's not needed" π |
It looks like this issue did not receive any activity for 60 days. It will be closed in 7 days if no further activity occurs. If you think your issue is still relevant, commenting will keep it open. Thanks! |
Anyone plans to solve this? |
https://stackoverflow.com/questions/78260818/set-expiration-and-automaticaly-refresh-jwt-token-in-nextauth-js/78260819#78260819 |
Can yo please mention the PR, as i am also stuck with the same issue, i want the session token to expire after 1 day instead of 30 days. |
It looks like this issue did not receive any activity for 60 days. It will be closed in 7 days if no further activity occurs. If you think your issue is still relevant, commenting will keep it open. Thanks! |
No Activity does not mean it's not needed π |
It looks like this issue did not receive any activity for 60 days. It will be closed in 7 days if no further activity occurs. If you think your issue is still relevant, commenting will keep it open. Thanks! |
To keep things tidy, we are closing this issue for now. If you think your issue is still relevant, leave a comment and we might reopen it. Thanks! |
Question π¬
Not saying my provider's token, my own generated JWT token with:

It doesn't seem to be handling it automatically, since I wait 30s and then get logged out
(I'm using Discord as auth provider)
How to reproduce βοΈ
None
Contributing ππ½
No, I am afraid I cannot help regarding this
The text was updated successfully, but these errors were encountered: