You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 14+Auth.js v5中间件重定向生效但浏览器URL未更新

问题:Next.js 14 + Auth.js v5 登录后URL未同步更新

环境与配置

使用Next.js v14和Auth.js v5实现认证,配置文件如下:

auth.config.ts

import type { NextAuthConfig } from 'next-auth';
import { NextResponse } from 'next/server';

export const authConfig = {
    pages: {
        signIn: '/login',
    },
    callbacks: {
        authorized({ auth, request: { nextUrl } }) {
            const isLoggedIn = !!auth?.user;
            const isProtectedRoute = nextUrl.pathname.startsWith('/boards') || nextUrl.pathname.startsWith('/people');
            const callbackUrl = nextUrl.searchParams.get("callbackUrl");
            if (isProtectedRoute) {
                return isLoggedIn;
            } else if (isLoggedIn) {
                if (callbackUrl !== null) {
                    return NextResponse.redirect(callbackUrl);
                } else {
                    return NextResponse.redirect(new URL('/boards', nextUrl));
                }
            }
            return true;
        },
    },
    providers: [],
} satisfies NextAuthConfig;

middleware.ts

import NextAuth from 'next-auth';
import { authConfig } from './auth.config';

export default NextAuth(authConfig).auth;

export const config = {
    matcher: ['/boards/:path*', '/people/:path*', '/login']
};

问题现象

未登录时访问受保护路由(如http://localhost:4000/boards),会被重定向到登录页,URL显示为http://localhost:4000/login?callbackUrl=http%3A%2F%2Flocalhost%3A4000%2Fboards;登录后页面渲染/boards的内容,但浏览器URL仍停留在登录页地址,未同步更新。

问题原因

Auth.js的authorized回调设计仅用于授权判断(返回布尔值),若直接返回NextResponse.redirect,只会在服务端完成内容的内部重定向渲染,但不会触发浏览器地址栏的URL更新,导致内容与URL不一致。

解决方案

1. 修正authorized回调逻辑

让authorized仅处理授权判断,跳转逻辑交给Auth.js自动处理:

import type { NextAuthConfig } from 'next-auth';

export const authConfig = {
    pages: {
        signIn: '/login',
    },
    callbacks: {
        authorized({ auth, request: { nextUrl } }) {
            const isLoggedIn = !!auth?.user;
            const isProtectedRoute = nextUrl.pathname.startsWith('/boards') || nextUrl.pathname.startsWith('/people');
            
            // 受保护路由:未登录则返回false,Auth.js会自动跳转到登录页并携带callbackUrl
            if (isProtectedRoute) {
                return isLoggedIn;
            }
            
            // 已登录访问登录页:返回false,触发Auth.js自动重定向
            if (isLoggedIn && nextUrl.pathname === '/login') {
                return false;
            }
            
            return true;
        },
    },
    providers: [],
} satisfies NextAuthConfig;

2. 在登录页处理登录后的跳转

根据登录页的组件类型(客户端/服务端),手动处理callbackUrl的跳转:

客户端组件示例

'use client';

import { signIn, useSession } from 'next-auth/react';
import { useRouter, useSearchParams } from 'next/navigation';
import { useEffect } from 'react';

export default function LoginPage() {
    const { data: session } = useSession();
    const router = useRouter();
    const searchParams = useSearchParams();
    const callbackUrl = searchParams.get('callbackUrl') || '/boards';

    // 登录成功后跳转至目标页
    useEffect(() => {
        if (session) {
            router.push(callbackUrl);
        }
    }, [session, router, callbackUrl]);

    const handleLogin = async () => {
        await signIn('your-provider', { callbackUrl });
    };

    return (
        <div>
            <h1>登录</h1>
            <button onClick={handleLogin}>登录</button>
        </div>
    );
}

服务端组件示例

import { getSession, signIn, redirect } from 'next-auth/react';
import { cookies } from 'next/headers';

export default async function LoginPage({ searchParams }: { searchParams: { callbackUrl?: string } }) {
    const session = await getSession({ cookies });
    const callbackUrl = searchParams.callbackUrl || '/boards';

    // 已登录直接跳转
    if (session) {
        redirect(callbackUrl);
    }

    return (
        <div>
            <h1>登录</h1>
            <form action={async () => {
                'use server';
                await signIn('your-provider', { callbackUrl });
            }}>
                <button type="submit">登录</button>
            </form>
        </div>
    );
}

3. 优化Middleware匹配规则

扩大匹配范围,覆盖所有非静态资源路由:

import NextAuth from 'next-auth';
import { authConfig } from './auth.config';

export default NextAuth(authConfig).auth;

export const config = {
    matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

原理说明

Auth.js的authorized回调核心是授权判断:

  • 返回true:允许访问当前路由
  • 返回false:自动跳转到pages.signIn配置页面,并携带当前路由作为callbackUrl
  • 直接返回NextResponse.redirect属于服务端内部渲染重定向,不会触发浏览器URL更新,因此需要将跳转逻辑交给客户端或Auth.js的自动处理流程,确保URL与页面内容同步。

内容的提问来源于stack exchange,提问作者mattstuehler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 16:49:50