NextAuth.js登录成功后重定向失效问题求助
Next.js认证登录后未跳转至callbackUrl问题排查
问题场景
基于Next.js搭建应用,按官方流程添加认证层后,未登录访问首页会被正确重定向到登录页(URL携带callbackUrl参数),提交有效账号密码后,浏览器已生成有效的认证Cookie,但页面始终停留在登录页,未跳转到callbackUrl指定的首页。
相关代码配置
auth.ts
import { sql } from "@vercel/postgres" import bcrypt from "bcrypt" import type { User } from "lib/definitions" import NextAuth from "next-auth" import Credentials from "next-auth/providers/credentials" import { z } from "zod" import { authConfig } from "./auth.config" 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 } } return null }, }), ], })
auth.config.ts
import type { NextAuthConfig } from "next-auth" export const authConfig = { pages: { signIn: "/signIn", }, callbacks: { authorized({ auth, request: { nextUrl } }) { return !!auth?.user }, }, providers: [], } satisfies NextAuthConfig
middleware.ts
import NextAuth from 'next-auth'; import { authConfig } from './auth.config'; export default NextAuth(authConfig).auth; export const config = { matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'], };
登录表单(app/signIn/page.tsx)
"use client" import { useFormState } from "react-dom" import { Button } from "@/registry/new-york/ui/button" import { Input } from "@/registry/new-york/ui/input" import { signIn } from "../lib/actions" import { ActionResult } from "../sharedTypes" export function SignInForm() { const [result, action] = useFormState<ActionResult | undefined, FormData>( signIn, undefined ) return ( <form action={action}> <div className="grid gap-4"> <div> <Input id="email" placeholder="Email" type="email" name="email" /> </div> <div> <Input id="password" placeholder="Password" type="password" name="password" /> </div> <Button>Sign in</Button> <div className="flex h-8 items-end space-x-1" aria-live="polite" aria-atomic="true" > {result?.errorMessage && ( <> <p className="text-sm text-red-500">{result.errorMessage}</p> </> )} </div> </div> </form> ) }
服务端Action(lib/actions.ts)
"use server" import { signIn as signInAuth } from "@/auth" import { AuthError } from "next-auth" import { ActionResult } from "../sharedTypes" export async function signIn( prevState: ActionResult | undefined, formData: FormData ): Promise<ActionResult | undefined> { try { await signInAuth("credentials", formData) } catch (error) { if (error instanceof AuthError) { if (error.type === "CredentialsSignin") { return { errorMessage: "Invalid credentials" } } return { errorMessage: "Something went wrong" } } throw error } }
问题原因
服务端Action调用signInAuth时,未传递URL中携带的callbackUrl参数,导致NextAuth无法识别登录成功后应跳转的目标页面,因此停留在登录页。
修复步骤
1. 在登录表单中传递callbackUrl
登录页URL已包含callbackUrl参数,需将其取出并通过隐藏字段传递给服务端Action。修改app/signIn/page.tsx:
"use client" import { useFormState } from "react-dom" import { useSearchParams } from "next/navigation" // 新增:获取URL参数 import { Button } from "@/registry/new-york/ui/button" import { Input } from "@/registry/new-york/ui/input" import { signIn } from "../lib/actions" import { ActionResult } from "../sharedTypes" export function SignInForm() { const searchParams = useSearchParams() // 获取callbackUrl,默认跳转首页 const callbackUrl = searchParams.get("callbackUrl") || "/" const [result, action] = useFormState<ActionResult | undefined, FormData>( signIn, undefined ) return ( <form action={action}> {/* 新增隐藏字段,传递callbackUrl */} <input type="hidden" name="callbackUrl" value={callbackUrl} /> <div className="grid gap-4"> <div> <Input id="email" placeholder="Email" type="email" name="email" /> </div> <div> <Input id="password" placeholder="Password" type="password" name="password" /> </div> <Button>Sign in</Button> <div className="flex h-8 items-end space-x-1" aria-live="polite" aria-atomic="true" > {result?.errorMessage && ( <> <p className="text-sm text-red-500">{result.errorMessage}</p> </> )} </div> </div> </form> ) }
2. 在服务端Action中使用callbackUrl
修改lib/actions.ts,调用signInAuth时传入callbackUrl参数:
"use server" import { signIn as signInAuth } from "@/auth" import { AuthError } from "next-auth" import { ActionResult } from "../sharedTypes" export async function signIn( prevState: ActionResult | undefined, formData: FormData ): Promise<ActionResult | undefined> { try { const callbackUrl = formData.get("callbackUrl") as string | undefined await signInAuth("credentials", { ...Object.fromEntries(formData), callbackUrl: callbackUrl || "/", }) } catch (error) { if (error instanceof AuthError) { if (error.type === "CredentialsSignin") { return { errorMessage: "Invalid credentials" } } return { errorMessage: "Something went wrong" } } throw error } }
额外检查项
- 确认
auth.config.ts中pages.signIn的路径与实际登录页一致(当前配置为/signIn,需确保页面存在)。 - 检查middleware的matcher规则,避免拦截登录成功后的跳转请求。
- 确认浏览器Cookie中已生成
next-auth.session-token,且用户信息正确。
内容的提问来源于stack exchange,提问作者bssyy78
相关产品推荐
相关产品推荐

