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

React与Firebase开发:如何将邮箱验证链接改为邮箱OTP验证?

将邮箱验证链接改为OTP的修改方案

Firebase自带的sendEmailVerification方法仅支持发送验证链接,无法直接生成并发送邮箱OTP。要实现你需要的功能,有两种可行方案,以下是具体修改步骤:

方案一:基于Firebase无密码登录(推荐新手使用)

这种方案利用Firebase的无密码邮箱登录功能,通过自定义邮件模板,将登录链接中的验证码作为OTP展示给用户,用户输入该验证码即可完成邮箱验证。

步骤1:配置Firebase控制台

  • 进入Firebase控制台 → 你的项目 → Authentication → 登录方法
  • 启用「邮箱/密码」和「邮箱链接(无密码登录)」选项
  • 进入模板 → 选择「密码登录(链接)」模板,修改邮件内容,提取链接中的oobCode参数作为OTP(比如将链接替换为你的邮箱验证码是:{{actionCode}})

步骤2:修改前端代码

import { getAuth, createUserWithEmailAndPassword, sendSignInLinkToEmail, isSignInWithEmailLink, signInWithEmailLink } from "firebase/auth";

const auth = getAuth();
const actionCodeSettings = {
  url: `${window.location.origin}/verify-email`, // 你的验证页面地址
  handleCodeInApp: true,
};

// 注册函数
const signup = () => {
  createUserWithEmailAndPassword(auth, user.email, user.password)
    .then((userCredential) => {
      // 发送包含OTP的登录链接邮件
      return sendSignInLinkToEmail(auth, user.email, actionCodeSettings);
    })
    .then(() => {
      // 保存邮箱到本地存储,用于后续验证
      window.localStorage.setItem('emailForSignIn', user.email);
      alert("包含验证码的邮件已发送");
    })
    .catch((error) => {
      alert(error.message);
    });
};

// 验证页面的OTP验证函数(比如在/verify-email页面)
const verifyEmailWithOTP = (otp) => {
  const email = window.localStorage.getItem('emailForSignIn');
  // 构造包含OTP的登录链接
  const signInLink = `${window.location.origin}/verify-email?oobCode=${otp}`;
  
  if (isSignInWithEmailLink(auth, signInLink)) {
    signInWithEmailLink(auth, email, signInLink)
      .then((userCredential) => {
        // 登录成功后,邮箱自动标记为已验证
        window.localStorage.removeItem('emailForSignIn');
        alert("邮箱验证成功");
      })
      .catch((error) => {
        alert("验证码错误或已过期");
      });
  }
};

方案二:自定义OTP生成与验证(需要云函数)

这种方案需要自己生成OTP、存储验证信息,并通过Firebase云函数发送邮件,适合需要完全自定义OTP逻辑的场景。

步骤1:创建Firebase云函数发送邮件

首先安装nodemailer依赖:

npm install nodemailer

然后编写云函数(functions/index.js):

const functions = require("firebase-functions");
const nodemailer = require("nodemailer");
const admin = require("firebase-admin");
admin.initializeApp();

// 配置邮件发送器(使用Gmail或其他SMTP服务)
const transporter = nodemailer.createTransport({
  service: "Gmail",
  auth: {
    user: "your-email@gmail.com",
    pass: "your-app-password",
  },
});

// 生成6位OTP
const generateOTP = () => {
  return Math.floor(100000 + Math.random() * 900000).toString();
};

// 云函数:发送邮箱OTP
exports.sendEmailOTP = functions.https.onCall(async (data, context) => {
  const { email, uid } = data;
  const otp = generateOTP();
  const expirationTime = Date.now() + 15 * 60 * 1000; // 15分钟过期

  // 将OTP和过期时间存储到Firestore
  await admin.firestore().collection("emailOTPs").doc(uid).set({
    otp,
    expirationTime,
    email,
  });

  // 发送邮件
  const mailOptions = {
    from: "Your App <your-email@gmail.com>",
    to: email,
    subject: "你的邮箱验证码",
    text: `你的验证码是:${otp},15分钟内有效。`,
  };

  await transporter.sendMail(mailOptions);
  return { success: true };
});

步骤2:修改前端代码

import { getAuth, createUserWithEmailAndPassword } from "firebase/auth";
import { getFunctions, httpsCallable } from "firebase/functions";
import { getFirestore } from "firebase/firestore";

const auth = getAuth();
const functions = getFunctions();
const db = getFirestore();
const sendEmailOTP = httpsCallable(functions, "sendEmailOTP");

// 注册函数
const signup = () => {
  createUserWithEmailAndPassword(auth, user.email, user.password)
    .then((userCredential) => {
      const uid = userCredential.user.uid;
      // 调用云函数发送OTP
      return sendEmailOTP({ email: user.email, uid });
    })
    .then(() => {
      alert("验证码已发送至你的邮箱");
    })
    .catch((error) => {
      alert(error.message);
    });
};

// OTP验证函数
const verifyOTP = (otp) => {
  const user = auth.currentUser;
  if (!user) return;

  // 查询Firestore中的OTP信息
  db.collection("emailOTPs").doc(user.uid).get()
    .then((doc) => {
      if (!doc.exists) throw new Error("验证码不存在");
      const { otp: storedOTP, expirationTime } = doc.data();
      
      if (Date.now() > expirationTime) throw new Error("验证码已过期");
      if (otp !== storedOTP) throw new Error("验证码错误");

      // 验证成功,标记邮箱为已验证
      return user.updateEmailVerified(true);
    })
    .then(() => {
      // 删除已使用的OTP
      return db.collection("emailOTPs").doc(user.uid).delete();
    })
    .then(() => {
      alert("邮箱验证成功");
    })
    .catch((error) => {
      alert(error.message);
    });
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 01:40:47