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

Next-Auth EmailProvider令牌未使用即失效问题求助

Next-Auth魔法链接登录提示失效问题排查与修复

问题现象

  • 验证令牌已成功存入数据库,邮件发送流程正常
  • 点击魔法链接后触发错误提示:无法登录,登录链接已失效,可能已被使用或过期
  • 页面跳转至http://localhost:3000/api/auth/error?error=Verification

可能原因及修复方案

1. Postmark模板URL传递异常

检查Postmark模板中magic_link变量的渲染逻辑,确保完整输出NextAuth生成的验证URL,避免HTML编码、截断或路径修改导致链接失效。比如模板中需直接使用变量值,不要添加额外的转义处理。

2. 多数据库连接冲突

代码中同时使用了MongoDBAdapter和自定义的connectDatabase连接数据库,可能引发连接池冲突或数据读取不一致。

修复建议:统一使用MongoDBAdapter的连接实例,移除jwt回调中的自定义数据库连接:

async jwt({ token }) {
  const client = await mongoClientPromise;
  const dbUser = await client.db().collection('users').findOne({ email: token.email });
  if (dbUser) {
    token.user = dbUser;
  }
  return token;
}

3. JWT回调异步逻辑顺序问题

原代码中user.findOne使用.then()嵌套写法,可能导致异步执行顺序混乱,改用await语法确保逻辑同步:

// 原写法
await user.findOne({ email: token.email }).then((user) => { ... })
// 修改为
const dbUser = await user.findOne({ email: token.email });
if (dbUser) {
  token.user = {
    ...(token as UserToken).user,
    ...dbUser.toJSON(),
  } as User;
}

4. 验证令牌过期时间配置

检查NextAuth的会话/JWT过期时间配置,默认魔法链接有效期为1小时,若自定义过短会导致链接提前失效。可显式配置:

session: {
  strategy: "jwt",
  maxAge: 3600, // 保持默认1小时有效期
},
jwt: {
  maxAge: 3600,
},

5. 捕获详细错误日志

在NextAuth配置中添加error事件监听,打印验证阶段的具体错误信息,定位问题根源:

events: {
  async error({ error, message }) {
    console.error("NextAuth验证错误详情:", error, message);
  },
},

修改后关键代码示例

export const nextAuthOptions: NextAuthOptions = {
  adapter: MongoDBAdapter(mongoClientPromise),
  providers: [
    EmailProvider({
      server: {
        host: process.env.SMTP_HOST,
        port: Number(process.env.SMTP_PORT),
        auth: {
          user: process.env.SMTP_USER,
          pass: process.env.SMTP_PASSWORD,
        },
      },
      from: process.env.SMTP_FROM,
      sendVerificationRequest: async ({ identifier, url, provider }) => {
        const result = await postmarkClient.sendEmailWithTemplate({
          TemplateId: 35449071,
          To: identifier,
          From: provider.from,
          TemplateModel: {
            name: "Sam",
            magic_link: url,
          },
        });

        if (result.ErrorCode) {
          throw new Error(result.Message);
        }
      },
    }),
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
  ],
  callbacks: {
    async jwt({ token }) {
      const client = await mongoClientPromise;
      const dbUser = await client.db().collection('users').findOne({ email: token.email });
      if (dbUser) {
        token.user = dbUser;
      }
      return token;
    },
    async session({ session, token }) {
      delete token.password;
      return { ...session, ...token };
    },
  },
  events: {
    async error({ error, message }) {
      console.error("NextAuth验证错误:", error, message);
    },
  },
  session: {
    strategy: "jwt",
    maxAge: 3600,
  },
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 11:16:23