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

如何在NextAuth Email Provider注册时向MongoDB存储额外字段

问题

我创建了一个包含两个字段的登录表单:

  • 用户可选择所在大学的字段
  • 用户输入大学邮箱的字段

底层使用NextAuth的Email Provider,用户填写字段点击“注册”后,MongoDB的users集合会自动创建仅包含以下字段的默认文档:

email: 'theuseremail@something.com',
emailVerified: '2022-07-16T11:54:06.848+00:00'

同时用户会收到含魔法登录链接的邮件以登录网站。

我希望除了用户邮箱外,还能存储用户选择的大学,但找不到捕获并写入该数据的方法,恳请帮助。

附相关代码:

pages/api/[...nextAuth].js

import NextAuth from "next-auth"
import nodemailer from 'nodemailer'
import EmailProvider from 'next-auth/providers/email'
import { MongoDBAdapter } from "@next-auth/mongodb-adapter"
import clientPromise from "../../../utils/mongoClientPromise"

const THIRTY_DAYS = 30 * 24 * 60 * 60
const THIRTY_MINUTES = 30 * 60


export default NextAuth({
  secret: process.env.NEXTAUTH_SECRET,
  session: {
    strategy: 'jwt',
    maxAge: THIRTY_DAYS,
    updateAge: THIRTY_MINUTES
  },
  adapter: MongoDBAdapter(clientPromise),
  providers: [
    EmailProvider({
      server: {
        host: process.env.EMAIL_SERVER_HOST,
        port: process.env.EMAIL_SERVER_PORT,
        auth: {
          user: process.env.EMAIL_SERVER_USER,
          pass: process.env.EMAIL_SERVER_PASSWORD
        }
      },
      from: process.env.EMAIL_FROM,
      async sendVerificationRequest ({
        identifier: email,
        url,
        provider: { server, from }
      }) {
        const { host } = new URL(url)
        const transport = nodemailer.createTransport(server)
        await transport.sendMail({
          to: email,
          from,
          subject: `Sign in to ${host}`,
          text: text({ url, host }),
          html: html({ url, host, email })
        })
      }
    })
  ],
  pages: {
    signIn: '/login',
  }
})

function html ({ url, host, email }) {
  const escapedEmail = `${email.replace(/\./g, '​.')}`
  const escapedHost = `${host.replace(/\./g, '​.')}`
  // Your email template here
  return `
      <body>
        <h1>Your magic link! 🪄</h1>
        <h3>Your email is ${escapedEmail}</h3>
        <p>
          <a href="${url}">Sign in to ${escapedHost}</a>
      </body>
  `
}

// Fallback for non-HTML email clients
function text ({ url, host }) {
  return `Sign in to ${host}\n${url}\n\n`
}

pages/login.tsx

import { Row, Col, Button, Input, Form, Space } from "antd";
import { useSession, signIn } from "next-auth/react";
import { getCsrfToken } from "next-auth/react"
import { useRouter } from 'next/router';
import { useState } from "react";
import UniversitySearchAndSelectDropdown from "../components/UniversitySearchAndSelectDropdown";
import data from '../mock_api_payload.json'


export default function LoginPage({ csrfToken }) {
    const navigate = useRouter();
    const { data: session } = useSession()
    const [selectedUniversityId, setSelectedUniversityId] = useState('');
    const [form] = Form.useForm();

    if (session) {
        navigate.push("/")
    }
    if (!session) {
        return (
        <>
            <Row justify="center" style={{marginTop: '2rem'}}>
                <Col>
                <form method="post" action="/api/auth/signin/email">
                    <input name="csrfToken" type="hidden" defaultValue={csrfToken} />
                    <Row justify="center">
                        <Col>
                            <Space direction="vertical">
                                <Input 
                                    placeholder="Enter your university email address" 
                                    type="email" 
                                    id="email" 
                                    name="email" 
                                />
                            </Space>
                        </Col>
                    </Row>
                    <Row justify="center" style={{marginTop: '1rem'}}>
                        <Col>
                            <Button 
                                htmlType="submit" 
                                shape="round"
                                type="primary"
                            >Sign in</Button>
                        </Col>
                    </Row>
                </form>
                </Col>
            </Row>
        </>
        )
    }
};

export async function getServerSideProps(context: any) {
    const csrfToken = await getCsrfToken(context)
    return {
      props: { csrfToken },
    }
}

解决方案

要实现存储用户选择的大学,需要通过传递表单字段到验证链接,再在用户登录时更新数据库的方式完成,具体步骤如下:

1. 修改登录表单,提交大学字段

在login.tsx的表单中,添加隐藏字段传递选中的大学ID,确保表单提交时该字段与邮箱一起发送:

// 在login.tsx的form内,邮箱输入框下方添加以下代码
<Row justify="center" style={{marginTop: '1rem'}}>
  <Col>
    <Space direction="vertical">
      {/* 你的大学选择组件 */}
      <UniversitySearchAndSelectDropdown
        value={selectedUniversityId}
        onChange={setSelectedUniversityId}
      />
      {/* 隐藏字段,传递大学ID */}
      <input 
        type="hidden" 
        name="universityId" 
        value={selectedUniversityId} 
        required
      />
    </Space>
  </Col>
</Row>

2. 修改NextAuth配置,将大学ID附加到验证链接

在[...nextAuth].js的EmailProvider配置中,修改sendVerificationRequest方法,从请求体中获取大学ID并添加到魔法链接的查询参数中:

async sendVerificationRequest ({
  identifier: email,
  url,
  provider: { server, from },
  req // 添加req参数,用于获取表单提交的数据
}) {
  // 从表单提交的请求体中获取大学ID
  const universityId = req.body.universityId;
  // 将大学ID添加到魔法链接的查询参数中
  const magicUrl = new URL(url);
  magicUrl.searchParams.set('universityId', universityId);
  const updatedUrl = magicUrl.toString();

  const { host } = new URL(updatedUrl);
  const transport = nodemailer.createTransport(server);
  await transport.sendMail({
    to: email,
    from,
    subject: `Sign in to ${host}`,
    text: text({ url: updatedUrl, host }),
    html: html({ url: updatedUrl, host, email })
  })
}

3. 添加回调函数,登录时更新用户数据库

在[...nextAuth].js的NextAuth配置中添加callbacks,在用户通过魔法链接登录时,从查询参数中获取大学ID并更新MongoDB的用户文档:

// 在NextAuth配置对象中添加以下内容
callbacks: {
  async signIn({ user, account, req }) {
    // 仅处理Email Provider的登录请求
    if (account?.provider === "email" && user) {
      // 从请求的查询参数中获取大学ID
      const universityId = req.query.universityId;
      if (universityId) {
        // 连接MongoDB并更新用户文档
        const client = await clientPromise;
        const db = client.db();
        await db.collection('users').updateOne(
          { _id: user.id },
          { $set: { universityId: universityId } }
        );
      }
    }
    return true;
  }
},

完成以上步骤后,用户首次登录时,选择的大学ID会被自动写入MongoDB的users集合中,后续用户文档将包含email、emailVerified和universityId字段。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 21:57:42