Search code examples
next.jsnext-auth

Next auth refresh token strategy


Using next.js auth (next auth) I'm creating CredentialsProvider, trying to connect it to django backend. All is working good except refresh token strategy: after obtaining new access token, access token and expire date not saving. So next auth is sending refresh token every request. I don't know why, but I can't update token data after "first time token created".

Here is the code:

async function refreshAccessToken(token) {
  console.log('UPDATE')
  const refresh = await axios
    .post('/token/refresh/', {
      refresh: token.refreshToken
    })
    .catch((error) => {
      console.log(error)
    })
  if (refresh && refresh.status === 200 && refresh.data.access) {
    return {
      ...token,
      accessToken: refresh.data.access,
      expiresAt: Date.now() + 10 * 1000
    }
  }
  return {
    ...token,
    error: 'RefreshAccessTokenError'
  }
}

export default NextAuth({
  providers: [
    CredentialsProvider({
      id: 'credentials',
      name: 'my-project',
      async authorize(credentials) {
        const auth = await axios
          .post('/token/', {
            username: credentials.username,
            password: credentials.password
          })
          .catch((error) => {
            console.log(error)
          })

        if (auth.status === 200 && auth.data.access) {
          const profile = await axios
            .get('/v1/profile/', {
              headers: {
                Authorization: `Bearer ${auth.data.access}`
              }
            })
            .catch((error) => {
              console.log(error)
            })
          if (profile.status === 200 && profile.data) {
            return {
              ...profile.data,
              tokens: auth.data
            }
          }
        }
        return null
      }
    })
  ],
  pages: {
    signIn: '/login'
  },
  callbacks: {
    jwt: async ({ token, user, account }) => {
      if (account && user) {
        return {
          // works normally
          accessToken: user.tokens.access,
          refreshToken: user.tokens.refresh,
          expiresAt: Date.now() + 10 * 1000,
          user
        }
      }

      if (Date.now() < token.expiresAt) {
        return token
      }

      // token is obtaining in refreshAccessToken but not saved...
      // next request refreshAccessToken will be called again...
      return refreshAccessToken(token)
    },
    session: async ({ session, token }) => {
      session.user = token.user
      session.token = token.accessToken
      return session
    }
  },
  debug: true
})

Any help please. Here is simplified example:

    jwt: async ({ token, user, account }) => {
      console.log(token.expiresAt)
      // here token.expiresAt will NEVER be equals 'hey'
      if (account && user) {
        return {
          accessToken: user.tokens.access,
          refreshToken: user.tokens.refresh,
          expiresAt: Date.now() + 10 * 1000,
          user
        }
      }

      if (Date.now() < token.expiresAt) {
        return token
      }

      token.expiresAt = 'hey'

      return token
    }

Solution

  • It seems to be a next-auth bug which a lot of developers are experiencing even now. New refreshed token is just not being persisted and next-auth is always reusing the very first token received on the login. Here is discussions:

    https://github.com/nextauthjs/next-auth/issues/7558 https://github.com/nextauthjs/next-auth/discussions/6642