nextauthjs / nextauthjs/next-auth

next/link prefetch cause to infinite redirects when using `withAuth` middleware

Open
#6,979 3 comments 8 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

triage
Dominant language
TypeScript
Stars
28.4k
Forks
4k
PR merge metrics
No merged PRs in 30d

Description

Environment

nextjs13, nextauth@4.20.1

Reproduction URL

See [How to reproduce] & [Sugguestion]

Describe the issue

Vercel put out a warning here regarding using middleware for authentication.

General speaking, if the state of user authentication when next/link do prefetching versus when user actual clicking differs (IE: unauthenticated then user login in another tab), nextjs always use prefetched data which is redirect to /login page.
That combines with ?callbackUrl= params would leads to infinite redirections.

How to reproduce
  1. Create custom /login with callbackUrl params
export const getServerSideProps: GetServerSideProps = async (context) => {
  const session = await getServerSession(context.req, context.res, authConfig)
  if (session) {
    // redirect if authenticated
    return {
        redirect: {
          destination: context.query.calbackUrl ?? '/',
          permanent: false
        }
      }
  }
  // other stuffs ...
  
}
  1. Apply withAuth
export { default } from "next-auth/middleware"
  1. Add an next/link to protected route
<Link href={'/protected'}>Protected</Link>
  1. Open the page with the link, next/link prefetch will received a redirect due to user not authenticated
/protected -> /login?callbackUrl=/protected
  1. User do authentication in another tab then go back and click the link in the first one
    Cached response is used, leading to infinite redirects
/protected -> /login?callbackUrl=/protected -(as user already authenticated)-> 
  /protected -> /login?callbackUrl=/protected -> ...
Expected behavior

User should be redirected to /protected as usual.

Workarounds

Mentioned in the warning, dev can work around by render url based on user's authentication status

function ProtectedLink() {
    const {data: session} = useSession()
    return <Link href={session ? '/protected' : '/login?callbackUrl=/protected'} prefetch={!!session}}
}

But that would take aways most of the reasons to use middleware.

Suggestion

When withAuth failed to check user authentication (unauthenticated, error), don't cache the response.

headers.set('x-middleware-cache', 'no-cache')

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the withAuth middleware and next/link prefetch flow described in the reproduction; no repository file or test is named, so begin by reproducing the cross-tab authentication sequence. Done means the authenticated user can click the protected link and reach /protected without the cached redirect causing an infinite loop.

Written by the indexing model from the issue text.

Assessment

Tech stack
nextjs, react, typescript
Domain
authentication, frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.