Firebase手机号认证提交资料报auth/user-token-expired错误
问题原因
你碰到的auth/user-token-expired报错是因为当前登录用户的身份令牌已过期,Firebase判定会话失效,所以执行updateProfile、updateEmail这类需要鉴权的操作时会直接拦截,操作失败后会话自动销毁,就会出现用户被自动登出的情况。
你现有代码有三个直接触发问题的硬伤:
- 所有异步操作(更新资料、更新邮箱、调用signUp方法、页面跳转)全是同步并行执行的,根本没等前一步操作完成就跑下一段,甚至操作还没执行完就直接跳路由,很容易打断Firebase的鉴权状态校验流程,导致令牌提前失效
updateEmail属于Firebase Auth的高敏感操作,要求用户必须是最近刚完成登录的状态才能执行,手机号验证码登录完成后如果停留时间稍长、或者中间有其他状态变更,令牌就会过期,直接调接口必然报错- 输入框change事件里错误加了
e.preventDefault(),容易引发意外的页面行为打断鉴权流程;另外还冗余引入了lodash的update方法但从未使用,虽然不直接触发报错,但属于无意义的代码冗余。
修复方案
第一步:修正异步执行逻辑,所有操作按顺序等待执行结果
绝对不能在异步操作没完成的时候就跳页,必须等所有资料更新操作走完、确认成功之后再做路由跳转,把原来的并行逻辑改成串行async/await写法,修正后的核心方法代码如下:
// 改造邮箱更新方法,等待执行结果再返回 const updateEmailUser = async () => { try { await updateEmail(auth.currentUser, email); console.log("邮箱更新成功"); return true; } catch (error) { console.log("邮箱更新失败", error); // 捕获令牌过期、需要最近登录类的错误往上抛出 if (error.code === 'auth/user-token-expired' || error.code === 'auth/requires-recent-login') { throw error; } return false; } }; // 改造资料更新方法,先更新昵称再更新邮箱,全部执行完再返回 const updateUserProfile = async () => { try { await updateProfile(auth.currentUser, { displayName: name, }); console.log("资料更新成功", name, email); await updateEmailUser(); return true; } catch (error) { console.log("资料更新失败", error); throw error; } }; // 改造提交方法,全流程等待执行结果,失败就终止后续跳页逻辑 const handleSubmit = async (e) => { e.preventDefault(); try { // 等待用户资料更新完成 await updateUserProfile(); // 等待自定义signUp业务逻辑执行完成 await signUp(name, userType, email); // 所有操作成功后再跳转路由 const path = userType === "salonOwner" ? "/addBuisnessDetails" : "/salonsNearby"; navigate(path, { replace: true }); } catch (err) { // 碰到登录过期错误,弹出提示引导用户重新做身份验证即可 if (err.code === 'auth/user-token-expired' || err.code === 'auth/requires-recent-login') { console.log("登录状态已过期,请重新验证身份"); } console.log("提交失败", err); } };
第二步:处理敏感操作的最近登录校验要求
Firebase规定修改邮箱、修改密码、删除账号这类操作,必须要求用户在几分钟内刚完成登录鉴权才能执行,如果用户从手机号验证完成到填完表单提交的间隔太长,就会触发令牌过期。碰到这个情况不要直接报错登出,要调用reauthenticateWithCredential方法,用用户之前手机号验证环节留存的PhoneAuthProvider凭证重新做一次身份校验,拿到新的有效令牌之后再重新执行更新操作即可,这个流程不需要用户重复输入验证码,不会影响使用体验。
第三步:修正输入框事件的错误逻辑
把handleChange方法里多余的e.preventDefault()删掉,输入框输入时不需要阻止默认行为,避免意外的页面重载打断鉴权状态,修正后的代码如下:
const handleChange = (e) => { // 移除这里的e.preventDefault() const { name, value } = e.target; switch (name) { case "displayname": setname(value); break; case "email": setemail(value); break; default: break; } };
额外优化建议
- 删除代码里冗余引入、从未调用的lodash
update方法,减少不必要的打包体积和潜在变量冲突 - 可以在组件挂载时加
onAuthStateChanged监听,确认auth.currentUser确实存在且状态有效之后再渲染表单,避免用户已经登出的状态下还能进到这个页面提交表单 - 所有涉及Firebase Auth的操作,都要等异步返回结果之后再执行后续逻辑,不要提前跳页卸载组件,否则会强制中断正在发送的请求,极易触发令牌失效问题。
内容的提问来源于stack exchange,提问作者Arjav Sethi
相关产品推荐
相关产品推荐

