I have this code that supposedly accesses an external API using the Next.js SDK.
// app/api/test/route.ts (Typescript)
import { NextResponse } from 'next/server'
import { getAccessToken, withApiAuthRequired } from '@auth0/nextjs-auth0'
export const GET = withApiAuthRequired(async () => {
// If your access token is expired and you have a refresh token
// `getAccessToken` will fetch you a new one using the `refresh_token` grant
const { accessToken } = await getAccessToken()
const response = await fetch('https://my-api.my.com/test', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
})
const data = await response.text()
return NextResponse.json({ data })
})
The problem is, it doesn’t refresh the access token and rotate the refresh token. It is always trying to refresh with the expired token. I think it works exactly once, then from second time I get the error (in server):
- error AccessTokenError: The request to refresh the access token failed. CAUSE: invalid_grant (Unknown or invalid refresh token.)
at NodeClient.refresh (webpack-internal:///(rsc)/./node_modules/@auth0/nextjs-auth0/dist/auth0-session/client/node-client.js:158:19)
The only way to refresh the access token is to logout and log in again, which is obvouisly not ideal and totally defeats the purpose of refresh tokens.
There is no example to guide how to get rotating refresh tokens to work correctly in a Next.js 13 project using app router.
I am already using “Regular Web Application” for this. I have set the correct audience and scope correctly in environment, including offline_access. I’m lost how to solve this. Is using “reusable refresh token” the only way? There’s no way to use the rotating one?
PS: It works correctly in Postman when directly accessing https://my-api.my.com/test after setting up the OAuth 2.0 to authenticate with Auth0. So the problem is unlikely to be on my backend.
Noting here that I didn’t find any solution for the above. In the end, we just disabled “Refresh Token Rotation” because we don’t have a solution.
I hope the documentation can be improved and a solution can be made public by Auth0.
I’m a little late on the subject, but I think I’ve found an alternative to this problem.
Instead of using getAccessToken(), try using getSession(). This will retrieve the session access token (which has normally been refreshed).
The problem is that a token may expire while the background session remains active. In this case, the user will not be automatically redirected to the login page (because his session is active) and the token obtained by getAccessToken() will have expired.
We re-enabled Rotating Refresh Tokens, and now we’re facing the same problem again. I haven’t tried it in the API route (the code I shared in my original post) but when I tried using getSession().accessToken in a Server Action, it just expired after a given time, but didn’t refresh. After the expiry the whole authentication just started failing.
Anyway, the problem with getAccessToken() is that it doesn’t work correctly in Server Actions. It seems to always use the original session, even after a refresh. In fact, calling getSession() seems to always give the original session, not the updated session after a refresh, even if the cookies contain the updated session! This seems to be a bug in how the SDK handles server components (RSC), or it’s something weird with Vercel itself.
When refreshing tokens, you need to ensure that the session is updated to extend the session. You can do this by including the refresh: true parameter.