NextJS-NextAuth登录成功后无法重定向至/dashboard问题求助
问题描述
我已花费12小时解决同一问题,查阅了几乎所有论坛仍未找到适配方案。我按照NextAuth和NextJS官方教程配置认证功能,期望用户登录成功后重定向至/dashboard页面,但默认跳转至/login页面。我尝试修改middleware逻辑、重新验证路径,也试图理解其运行机制,但均未解决问题。我了解callbackurl选项,但它不符合我的表单逻辑,现求助解决方案。
相关代码文件
loginForm.ts
'use client'; import { useFormState, useFormStatus } from 'react-dom'; import Link from 'next/link'; import { authenticate } from '@/app/actions/login/loginEngine'; import ErrorAlert from '../errorAlert'; import { Button } from '../button'; export default function LoginForm() { const [errorMessage, dispatch] = useFormState(authenticate, undefined); return ( <> {errorMessage && ( <ErrorAlert AlertMessage={errorMessage} /> )} <div className="flex min-h-full flex-1 items-center justify-center px-4 py-12 sm:px-6 lg:px-8"> <div className="w-full max-w-sm space-y-10"> <div> {/* Include your entreprise logo here */} <h2 className="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900"> Sign in to your account </h2> </div> <form className="space-y-6" action={dispatch}> <div className="relative -space-y-px rounded-md shadow-sm"> <div className="pointer-events-none absolute inset-0 z-10 rounded-md ring-1 ring-inset ring-gray-300" /> <div> <label htmlFor="email-address" className="sr-only"> Email address </label> <input id="email-address" name="email" type="email" autoComplete="email" required minLength={6} className="relative block w-full rounded-t-md border-0 pl-4 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-100 placeholder:text-gray-400 focus:z-10 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" placeholder="Email address" /> </div> <div> <label htmlFor="password" className="sr-only"> Password </label> <input id="password" name="password" type="password" autoComplete="current-password" required className="relative block w-full rounded-b-md border-0 pl-5 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-100 placeholder:text-gray-400 focus:z-10 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" placeholder="Password" /> </div> </div> <div className="flex items-center justify-between"> <div className="flex items-center"> <input id="remember-me" name="remember-me" type="checkbox" className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600" /> <label htmlFor="remember-me" className="ml-3 block text-sm leading-6 text-gray-900"> Remember me </label> </div> <div className="text-sm leading-6"> <a href="#" className="font-semibold text-indigo-600 hover:text-indigo-500"> Forgot password? </a> </div> </div> <LoginButton /> </form> <p className="text-center text-sm leading-6 text-gray-500"> Not a member?{' '} <Link href="/register" className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500">Create an account</Link> </p> </div> </div> </> ) } function LoginButton() { const { pending } = useFormStatus(); return ( <div> <Button className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" aria-disabled={pending}> Sign in </Button> <button > Log in </button> </div> ); }
loginEngine.ts
'use server' import { AuthError } from 'next-auth'; import { signIn } from '../../../auth'; import { revalidatePath } from 'next/cache'; export async function authenticate( prevState: string | undefined, formData: FormData, ) { try { await signIn('credentials', formData); } catch (error) { if (error instanceof AuthError) { switch (error.type) { case 'CredentialsSignin': return 'Invalid credentials.'; default: return 'Something went wrong.'; } } throw error; } }
auth.ts
import NextAuth from 'next-auth'; import { authConfig } from './auth.config'; import Credentials from 'next-auth/providers/credentials'; import { z } from 'zod'; import bcrypt from 'bcrypt'; import { getUser } from '@/app/libs/dbCore'; export const { auth, signIn, signOut } = NextAuth({ ...authConfig, providers: [ Credentials({ async authorize(credentials) { const parsedCredentials = z .object({ email: z.string().email(), password: z.string().min(6) }) .safeParse(credentials); if (parsedCredentials.success) { const { email, password } = parsedCredentials.data; const user = await getUser(email); if (!user) return null; const passwordsMatch = await bcrypt.compare(password, user.password); if (passwordsMatch) return user; } console.log('Invalid credentials'); return null; }, }), ], });
auth.config.ts
import { NextAuthConfig } from "next-auth"; export const authConfig = { pages: { signIn: '/login', signOut: '/' }, callbacks: { authorized({ auth, request: { nextUrl } }) { const isLoggedIn = !!auth?.user; const isOnDashboard = nextUrl.pathname.startsWith('/dashboard'); const isOnSettings = nextUrl.pathname.startsWith('/settings') if (isOnDashboard || isOnSettings) { if (isLoggedIn) return true; return false; // Redirect unauthenticated users to login page } else if (isLoggedIn) { return Response.redirect(new URL('/dashboard', nextUrl)); } return true; }, }, providers: [], // Add providers with an empty array for now } satisfies NextAuthConfig;
middleware.ts
import NextAuth from 'next-auth'; import { authConfig } from './auth.config'; export default NextAuth(authConfig).auth; export const config = { // https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher matcher: ['/((?!api|_next/static|_next/image|.*\.png$).*)'], };
解决方案
1. 手动在登录成功后触发重定向
修改loginEngine.ts,在signIn成功后使用Next.js的redirect方法直接跳转到/dashboard,绕过默认回调逻辑:
'use server' import { AuthError } from 'next-auth'; import { signIn } from '../../../auth'; import { redirect } from 'next/navigation'; // 引入redirect export async function authenticate( prevState: string | undefined, formData: FormData, ) { try { await signIn('credentials', formData); // 登录成功后立即重定向到dashboard redirect('/dashboard'); } catch (error) { if (error instanceof AuthError) { switch (error.type) { case 'CredentialsSignin': return 'Invalid credentials.'; default: return 'Something went wrong.'; } } throw error; } }
2. 清理冗余按钮
LoginButton组件存在两个登录按钮,易造成交互混淆,删除多余的按钮:
function LoginButton() { const { pending } = useFormStatus(); return ( <div> <Button className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" aria-disabled={pending}> Sign in </Button> </div> ); }
3. 验证middleware逻辑
当前auth.config.ts中的authorized回调逻辑已正确配置:已登录用户访问非dashboard/settings页面时会自动重定向到dashboard。无需修改,确保middleware.ts的匹配规则覆盖所有需要验证的路由即可。
原理说明
默认情况下,signIn成功后会跳转到当前登录页/login,即使authorized回调会触发重定向,也可能存在延迟或优先级问题。通过在authenticate函数中手动调用redirect,可在登录成功后直接触发跳转,避免默认回跳逻辑,确保用户直接进入/dashboard。
内容的提问来源于stack exchange,提问作者pish
相关产品推荐
相关产品推荐

