Firebase重置密码:如何拦截流程以禁用历史密码?
用Cloud Functions实现Firebase密码重置时禁用历史密码
明确说:Firebase原生的密码重置流程(邮件链接类型)没法直接拦截它的内置重置函数,但完全可以用Cloud Functions配合自定义逻辑实现历史密码校验的需求,具体方案如下:
核心思路
Firebase密码重置最终会触发用户密码更新,我们可以监听这个密码更新事件,在密码变更完成后校验新密码是否属于历史哈希;或者自定义部分重置流程,提前完成校验逻辑。
具体实现步骤
1. 存储用户历史密码哈希
- 用户每次设置/修改密码时,把密码的哈希值存储到Firebase数据库(Firestore或实时数据库均可),与用户UID关联绑定。
- 重点:绝对不能存储明文密码,建议使用Firebase Auth自带的
scrypt哈希算法,通过Cloud Functions获取用户的密码哈希参数,生成与系统一致的哈希值。
2. 用Cloud Functions监听密码更新事件
通过onUserUpdated触发器监听用户密码变更动作,触发后执行历史密码校验:
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); exports.checkHistoryPassword = functions.auth.user().onUserUpdated(async (change, context) => { const newUser = change.after; const oldUser = change.before; // 仅当密码确实发生变更时执行校验 if (newUser.passwordHash !== oldUser.passwordHash) { const uid = newUser.uid; // 从数据库拉取该用户的历史哈希列表 const userHistoryRef = admin.firestore().collection("userPasswordHistory").doc(uid); const historyDoc = await userHistoryRef.get(); if (historyDoc.exists) { const storedHashes = historyDoc.data().hashes; // 检查新密码哈希是否存在于历史列表中 if (storedHashes.includes(newUser.passwordHash)) { // 匹配到历史密码,强制回滚至原密码 await admin.auth().updateUser(uid, { passwordHash: oldUser.passwordHash, passwordSalt: oldUser.passwordSalt }); functions.logger.error(`用户 ${uid} 尝试使用历史密码,已执行回滚`); } else { // 新密码合法,将哈希加入历史列表 await userHistoryRef.set({ hashes: admin.firestore.FieldValue.arrayUnion(newUser.passwordHash) }, { merge: true }); } } else { // 用户首次设置密码,初始化历史哈希列表 await userHistoryRef.set({ hashes: [newUser.passwordHash] }); } } });
3. 可选:自定义密码重置流程
如果想在用户输入新密码时实时完成校验(避免事后回滚的体验问题),可以放弃Firebase原生的重置邮件链接,改为:
- 发送自定义重置邮件,内含带过期时间的自定义令牌(存储在数据库中)。
- 用户访问自定义重置页面,输入新密码后先调用Cloud Functions校验哈希是否在历史列表中。
- 校验通过后,再调用
admin.auth().updateUser()更新密码,同时更新历史哈希列表。
关键注意事项
- 配置数据库安全规则,确保只有用户本人或授权管理员能访问历史哈希数据。
- 必须使用Firebase Auth原生哈希算法,避免自定义哈希导致与系统生成值不一致。
- 处理并发场景,比如用户快速多次修改密码时,保证哈希列表更新逻辑不冲突。
内容的提问来源于stack exchange,提问作者Mahesh H Viraktamath
相关产品推荐
相关产品推荐

