Infinite loop on refresh token endpoint in version 9.0.1
Environment
- Operating System: Linux
- Node Version: v20.17.0
- Nuxt Version: 3.13.0
- CLI Version: 3.13.1
- Nitro Version: 2.9.7
- Package Manager: [email protected]
- Builder: -
- User Config: -
- Runtime Modules: -
- Build Modules: -
Nuxt auth version: 9.0.1
Reproduction
With the configuration below, sign in to the app and reload the page
const determineBaseUrl = () => {
const baseUrl = process.env.NUXT_PUBLIC_BASE_API || '/api'
// Check if the baseUrl finishes with a trailing slash
return !baseUrl.endsWith('/') ? `${baseUrl}/auth/` : `${baseUrl}auth/`
}
const authOptions: ModuleOptions = {
baseURL: determineBaseUrl(),
globalAppMiddleware: true,
isEnabled: true,
provider: {
endpoints: {
getSession: { method: 'get', path: `session` },
signIn: { method: 'post', path: `credentials` },
signOut: { method: 'delete', path: `logout` },
// @ts-expect-error disable sign up, like in the example from the docs: https://auth.sidebase.io/guide/local/quick-start#api-endpoints
signUp: false,
},
pages: {
login: '/login',
},
// See: https://github.com/sidebase/nuxt-auth/issues/867#issuecomment-2293906780
refresh: {
endpoint: { method: 'post', path: `refresh-token` },
isEnabled: true,
refreshOnlyToken: true,
token: {
cookieName: 'auth.refresh',
httpOnlyCookieAttribute: false,
maxAgeInSeconds: 2592000,
refreshRequestTokenPointer: '/refreshToken',
sameSiteAttribute: 'lax',
secureCookieAttribute: false,
signInResponseRefreshTokenPointer: '/refreshToken',
},
},
session: {
dataType: {
companyHasAcceptedTermsOfUse: 'boolean',
companyId: 'number',
companyName: 'string',
email: 'string',
firstName: 'string',
fullyConnected: 'boolean',
id: 'string',
isTwoFactorInitialized: 'boolean',
lastName: 'string',
roles: '(\'admin\' | \'member\')[]',
username: 'string',
},
},
token: {
cookieName: 'auth.token',
headerName: 'Authorization',
httpOnlyCookieAttribute: false,
maxAgeInSeconds: 15 * 60, // 15 minutes
sameSiteAttribute: 'lax',
secureCookieAttribute: false,
signInResponseTokenPointer: '/accessToken',
},
type: 'local',
},
sessionRefresh: {
enableOnWindowFocus: true, // disable to avoid conflicts when switching tabs
enablePeriodically: 5 * 60 * 1000, // every 5 minutes
},
}
Describe the bug
The /refresh-token route is called a loop. The first calls refresh the token and then saturate the backend.
Cookies seems to be correctly set
Additional context
No response
Logs
No response
This is caused by your refresh.token.maxAgeInSeconds, which is 2592000.
Transformed in milliseconds by the DefaultRefreshHandler, this means 2_592_000_000, which is higher than the maximum number allowed by JS for setInterval, which is 2_147_483_647. So the value overflows, goes to something negative (or maybe zero?) and the interval is triggered without any pauses.
I fixed this with PR#891 (link).
Meanwhile you can decrease your refresh token maxAge to a value less than 24.8 days or use the temporary package, that contains the fix, until PR#891 is merged & released:
pnpm add https://pkg.pr.new/@sidebase/nuxt-auth@b41f424
Omg thanks for the explanation.
Let me try it the next week 🤞
Now, I'm trying with a light config as possible (see below) and I haven't the loop on the refresh (thanks!), but the refreshing doesn't work well.
For eg. if I remove the cookie auth.token (and have a valid auth.refresh-token), after reloading the page, I'm redirected to the login page instead of having a new refreshed token set...
Also, I disabled the SSR to have a good view of what happened and I don't see any request to refresh the token... 😓 (this test was OK with the 0.8.2 version)
I tried a trick to refresh the token when a refresh-token is detected but the (access) token not:
const { refresh } = useAuth()
const refreshCookie = useCookie('auth.refresh-token')
const tokenCookie = useCookie('auth.token')
// If needed, refresh the token
if (refreshCookie.value && !tokenCookie.value) {
await refresh()
}
The behavior is strange because I can log the refreshCookie cookie (which is valid) but a null value is sent to my refresh route...
Configuration
const authOptions: ModuleOptions = {
baseURL: determineBaseUrl(),
globalAppMiddleware: true,
isEnabled: true,
provider: {
endpoints: {
getSession: { method: 'get', path: `session` },
signIn: { method: 'post', path: `credentials` },
signOut: { method: 'delete', path: `logout` },
// @ts-expect-error disable sign up, like in the example from the docs: https://auth.sidebase.io/guide/local/quick-start#api-endpoints
signUp: false,
},
pages: {
login: '/login',
},
// See: https://github.com/sidebase/nuxt-auth/issues/867#issuecomment-2293906780
refresh: {
endpoint: { method: 'post', path: `refresh-token` },
isEnabled: true,
},
session: {
dataType: {
companyHasAcceptedTermsOfUse: 'boolean',
companyId: 'number',
companyName: 'string',
email: 'string',
firstName: 'string',
fullyConnected: 'boolean',
id: 'string',
isTwoFactorInitialized: 'boolean',
lastName: 'string',
roles: '(\'admin\' | \'member\')[]',
username: 'string',
},
},
token: {
signInResponseTokenPointer: '/accessToken',
},
type: 'local',
},
}
Numbers are in milliseconds. Seems like a typo... A temporary fix for me was to multiply numbers to 60 * 1000
Now, I'm trying with a light config as possible (see below) and I haven't the loop on the refresh (thanks!), but the refreshing doesn't work well.
For eg. if I remove the cookie
auth.token(and have a validauth.refresh-token), after reloading the page, I'm redirected to the login page instead of having a new refreshed token set...
Indeed, this happens to me too: when the refresh token is there and the auth one is missing, the refresh will not be triggered.
The error seems to be related to the refresh-token server this time - I will propose a fix soon.
@Suniron the second part of the issue will be fixed by PR#902.