i am currently working on a nuxt application, which i need to integrate with a custome provider for authentication(OAuth). for authentication I am using next-auth(AuthJs), All is working fine, when it comes to refreshing the access_token
which is necessary for this application it fails. The validity of access_token
is 2 hours
in the jwt
callback, after assigning the refreshed_token
to the token
and passed to the sesssion
callback. But in the next session check(which automatically runs) the token in the jwt callback has the older values. The assigned values of initial sign in is persisted. but as for the refreshing it becomes an issue.
As the value is not re-assigned with the refresh_token
so is the expire_at
attribute. So it will keep refreshing the token until it makes the initial refresh_token
invalid.
Is there any workaround/solutions to this problem?
why the values(returned) in the initial login (which is checking for an account
param in the callback) working well and the token is being persisted?
server/api/auth/[...].ts
import type { AuthConfig, TokenSet } from "@auth/core/types";
import { NuxtAuthHandler } from "#auth";
import { JWT } from "@auth/core/jwt";
const runtimeConfig = useRuntimeConfig();
const BASE_URL = "Base url";
async function refreshAccessToken(token: JWT): Promise<TokenSet> {
const params = new URLSearchParams({
client_id: process.env.NUXT_AUTH_CLIENT_ID!,
client_secret: process.env.NUXT_AUTH_CLIENT_SECRET!,
grant_type: "refresh_token",
refresh_token: token.refresh_token as string,
});
const response = await fetch(`${BASE_URL}/oauth/token`, {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: params.toString(),
method: "POST",
});
const tokens: TokenSet = await response.json();
if (!response.ok) {
throw tokens;
}
return {
...token,
access_token: tokens.access_token,
refresh_token: tokens.refresh_token,
error: null,
expires_at: Math.floor(Date.now() / 1000 + 30), // 30 for testing
// expires_at: Math.floor(Date.now() / 1000 + tokens.expires_in!),
};
}
export const authOptions: AuthConfig = {
secret: runtimeConfig.authJs.secret,
debug: true,
callbacks: {
async jwt({ token, account }) {
console.log('-----------------------------------');
console.log("token in jwt is ", token);
const now = Date.now();
let resToken = null;
if (account) {
console.log("initial login", token, account);
// Initial login
resToken = {
...token,
access_token: account.access_token,
refresh_token: account.refresh_token,
expires_at: Math.floor(Date.now() / 1000 + 30),
// expires_at: Math.floor(Date.now() / 1000 + account.expires_in!),
error: null,
};
} else if (now < (token.expires_at as number) * 1000) {
console.log("token is valid ", token);
// Have valid token
resToken = token;
} else {
// RefreshToken
try {
console.log("before refresh token is ", token);
// the returned refreshed token is not persisting so expires_at, access_token and refresh_token remains the same as the first
// So this code blocks keeps running for around 3 times as the expires_at not changed
// it will invalidate the accessToken and refresh token it becomes a "RefreshAccessTokenError"
resToken = await refreshAccessToken(token);
console.log("resToken is ", resToken);
console.log('-----------------------------------');
} catch (error) {
console.error("Error refreshing access token", error);
return { ...token, error: "RefreshAccessTokenError" as const };
}
}
return resToken;
},
async session({ session, token }) {
return {
...session,
access_token: token.access_token,
refresh_token: token.refresh_token,
error: token.error,
};
},
},
providers: [
{
id: "projectId",
name: "projectName",
type: "oauth",
issuer: BASE_URL,
clientId: process.env.NUXT_AUTH_CLIENT_ID,
clientSecret: process.env.NUXT_AUTH_CLIENT_SECRET,
token: `${BASE_URL}/oauth/token`,
authorization: {
url: `${BASE_URL}/oauth/authorize`,
params: { scope: "api_v3" },
},
redirectProxyUrl: "http://localhost:3000/api/auth",
userinfo: `${BASE_URL}/api/v3/users/me`,
},
],
};
export default NuxtAuthHandler(authOptions, runtimeConfig);