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

Next.js 14中React Hook Form与Next Auth登录集成问题

Next.js 14 整合 React Hook Form + Next Auth (Credentials 提供者) 并保留 Zod 类型

问题1:React Hook Form 提交无网络请求

React Hook Form 默认会阻止表单默认提交行为,且不会自动发起请求到 Next Auth 接口。解决方式是在表单提交时手动调用 Next Auth 的 signIn 方法,传递验证后的表单数据。

问题2:Next Auth 报错 "Callback for provider type credentials not supported"

Next Auth v5(适配 Next.js 14 的主流版本)对 Credentials 提供者的配置逻辑做了调整,不再支持旧版的 callbacks 配置,改用 authorize 函数处理用户验证逻辑。

完整集成方案(含 Zod 类型支持)

1. 定义 Zod 表单 Schema

创建 Zod 校验规则并生成对应 TypeScript 类型,确保前后端类型一致:

// lib/schemas.ts
import { z } from "zod";

export const loginSchema = z.object({
  email: z.string().email("请输入有效的邮箱地址"),
  password: z.string().min(6, "密码至少需要6个字符"),
});

export type LoginFormData = z.infer<typeof loginSchema>;

2. 编写登录页面(客户端组件)

使用 React Hook Form 结合 Zod 做表单校验,提交时调用 signIn 方法:

// app/login/page.tsx
"use client";

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { signIn } from "next-auth/react";
import { loginSchema, LoginFormData } from "@/lib/schemas";
import { useRouter } from "next/navigation";

export default function LoginPage() {
  const router = useRouter();
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
  } = useForm<LoginFormData>({
    resolver: zodResolver(loginSchema),
    defaultValues: { email: "", password: "" },
  });

  const onSubmit = async (data: LoginFormData) => {
    const result = await signIn("credentials", {
      ...data,
      redirect: false, // 手动控制跳转,避免默认行为
    });

    if (result?.ok) {
      router.push("/dashboard");
    } else {
      console.error("登录失败:", result?.error);
    }
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)} className="space-y-4 max-w-md mx-auto mt-8">
      <div>
        <label htmlFor="email" className="block text-sm font-medium mb-1">邮箱</label>
        <input
          {...register("email")}
          type="email"
          id="email"
          className="w-full px-3 py-2 border rounded-md"
        />
        {errors.email && <p className="text-red-500 text-xs mt-1">{errors.email.message}</p>}
      </div>
      <div>
        <label htmlFor="password" className="block text-sm font-medium mb-1">密码</label>
        <input
          {...register("password")}
          type="password"
          id="password"
          className="w-full px-3 py-2 border rounded-md"
        />
        {errors.password && <p className="text-red-500 text-xs mt-1">{errors.password.message}</p>}
      </div>
      <button
        type="submit"
        disabled={isSubmitting}
        className="w-full py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50"
      >
        {isSubmitting ? "登录中..." : "登录"}
      </button>
    </form>
  );
}

3. 配置 Next Auth(Credentials 提供者)

在 lib/auth.ts 中配置 Credentials 提供者,使用 authorize 函数处理用户验证,同时用 Zod 校验传入的 credentials:

// lib/auth.ts
import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import { loginSchema } from "@/lib/schemas";
import { compare } from "bcryptjs";

// 替换为你的实际数据库查询逻辑
async function getUserByEmail(email: string) {
  // 示例:模拟查询用户
  return {
    id: "1",
    email: "user@example.com",
    password: "$2a$10$rOvHdKzn7V8eU8LbKXsYqOeKXsYqOeKXsYqOeKXsYqOeKXsYqOeK", // bcrypt哈希后的密码
    name: "测试用户",
  };
}

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    Credentials({
      async authorize(credentials) {
        // 用Zod验证传入的凭证
        const validationResult = loginSchema.safeParse(credentials);
        if (!validationResult.success) return null;

        const { email, password } = validationResult.data;
        const user = await getUserByEmail(email);
        if (!user) return null;

        // 验证密码是否匹配
        const isPasswordValid = await compare(password, user.password);
        if (!isPasswordValid) return null;

        // 返回用户信息(不要包含密码等敏感数据)
        return { id: user.id, email: user.email, name: user.name };
      },
    }),
  ],
  session: { strategy: "jwt" }, // Credentials提供者必须使用JWT会话策略
  pages: { signIn: "/login" }, // 指定自定义登录页面
});

4. 创建 Next Auth 路由处理器

// app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/lib/auth";
export const { GET, POST } = handlers;

关键注意事项

  • 确保使用 Next Auth v5,v4 的配置逻辑不同,会导致兼容性问题。
  • Credentials 提供者必须搭配 jwt 会话策略,因为没有第三方 OAuth 提供商来托管会话。
  • 在 authorize 函数中,必须返回用户对象(或 null),不能使用旧版的 callbacks.jwt 或 callbacks.session 来处理凭证验证。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 20:49:58