如何在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, '&#8203;.')}` const escapedHost = `${host.replace(/\./g, '&#8203;.')}` // 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
相关产品推荐
相关产品推荐

