Refresh Token Rotation
Obtain new access token from refresh token.
NextAuth.js does not handle refresh token rotation automatically. But the functionality can be implemented using callbacks.
- Create a
`refreshAccessToken`
helper.
pages/api/auth/[...nextauth].js
// Helper to obtain a new access_token from a refresh token.async function refreshAccessToken(token) { try { const formData = new URLSearchParams()
formData.append("grant_type", "refresh_token") formData.append("client_id", process.env.OAUTH_CLIENT_ID) formData.append("client_secret", process.env.OAUTH_CLIENT_SECRET) formData.append("refresh_token", token.refreshToken)
const response = await fetch( `${process.env.NEXT_PUBLIC_DRUPAL_BASE_URL}/oauth/token`, { method: "POST", body: formData, headers: { "Content-Type": "application/x-www-form-urlencoded", }, } )
const data = await response.json()
if (!response.ok) { throw new Error() }
return { ...token, accessToken: data.access_token, accessTokenExpires: Date.now() + data.expires_in * 1000, refreshToken: data.refresh_token ?? token.refreshToken, } } catch (error) { return { ...token, error: "RefreshAccessTokenError", } }}
- Update our
`jwt`
and`session`
callbacks to handle refresh tokens.
pages/api/auth/[...nextauth].js
export default NextAuth({ callbacks: { async jwt({ token, user }) { // Forward the access token, refresh token and expiration date from user. // We'll use these to handle token refresh. if (user) { token.accessToken = user.access_token token.accessTokenExpires = Date.now() + user.expires_in * 1000 token.refreshToken = user.refresh_token }
// If token has not expired, return it, if (Date.now() < token.accessTokenExpires) { return token }
// Otherwise, refresh the token. return refreshAccessToken(token) }, async session({ session, token }) { if (token?.accessToken) { session.accessToken = token.accessToken
// Decode token and pass info to session. const decoded = jwt_decode(token.accessToken) session.user.id = decoded.id session.user.email = decoded.email session.user.username = decoded.username session.error = token.error } return session }, },})