如何在Firebase的applyActionCode后安全获取用户邮箱?
问题背景与核心诉求
我有自行搭建的用户记录服务器,业务流程为:用户注册后,后端通过FirebaseAuth.getInstance().createCustomToken(id, additionalClaims)生成自定义令牌,前端用该令牌调用signInWithCustomToken登录,再调用sendEmailVerification发送验证邮件。
前端注册后执行登录并发送验证邮件的代码:
await axios .post(`/authorization/sign-up`, { username: inputData.username, password: inputData.password, }) .then(async (response) => { const auth = getAuth(); signInWithCustomToken(auth, response.data.content.token) .then(async(userCredential) => { await updateEmail(userCredential.user, inputData.username) const actionCodeSettings = { url: `https://domain host/test?email=${inputData.username}` }; await sendEmailVerification(userCredential.user, actionCodeSettings) navigate("/"); //跳转至登录页 }) }) .catch((error) => toast(error.message)); } else { toast("Please input valid data"); }
后端登录验证逻辑:查询数据库时要求用户邮箱已验证,SQL语句如下:
select * from user where user = foo and password = foo and isEmailVerified = true
用户点击验证链接后跳转至verify-email页面,页面代码:
console.log("verifying email") const mode = searchParams.get('mode') // mode = verifyEmail const oobCode = searchParams.get('oobCode') const auth = getAuth() applyActionCode(auth, oobCode).then(() => { // 如何安全获取用户邮箱并更新后端数据库状态? })
核心问题:applyActionCode返回Promise<void>,跨设备验证时无法获取currentUser,且不能将邮箱放在continueUrl中(存在篡改风险),需要解决:如何在applyActionCode后安全获取用户邮箱?或者是否需要跳转回登录页,每次登录时对比decodedToken.isEmailVerified与数据库状态?
解决方案
方案一:解析oobCode获取用户信息
Firebase的oobCode本身包含用户身份信息,可通过后端Firebase Admin SDK解析该代码获取用户邮箱:
- 在verify-email页面,将oobCode发送到你的后端接口
- 后端使用
auth.checkActionCode(oobCode)方法解析代码,该方法会返回包含用户邮箱的对象,示例后端Java代码:
ActionCodeInfo codeInfo = FirebaseAuth.getInstance().checkActionCode(oobCode); String userEmail = codeInfo.getEmail(); // 用该邮箱更新数据库中用户的isEmailVerified状态为true
- 后端更新数据库后,返回成功状态给前端,前端再执行跳转等操作
这种方式通过官方SDK解析验证代码,完全避免了前端传递邮箱的篡改风险,安全可靠。
方案二:登录时同步验证状态
如果不想额外处理oobCode的后端解析,可采用登录时同步的逻辑:
- 用户完成邮箱验证后,前端跳转至登录页
- 用户登录时,后端验证账号密码的同时,调用Firebase Admin SDK的
auth.getUserByEmail(email)获取用户的isEmailVerified状态 - 对比数据库中的
isEmailVerified状态:若Firebase侧已验证但数据库未更新,则同步数据库状态为true后允许登录;若Firebase侧未验证,则拒绝登录并提示用户激活邮箱
这种方式无需在验证页面额外处理,逻辑简洁,但每次登录需多一次Firebase查询操作,适合对实时性要求不高的场景。
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

