如何检测用户在WebOTP API授权弹窗中点击拒绝按钮?
检测WebOTP API用户拒绝权限操作的方法
WebOTP API当前的规范设计中,用户点击权限弹窗的「拒绝」按钮时,navigator.credentials.get返回的Promise既不会resolve也不会reject,无法直接捕获该操作。但可以通过以下两种间接方式来处理这个场景:
1. 结合AbortController设置超时
用户拒绝后Promise会处于挂起状态,我们可以设置一个超时时间,超时后主动中止请求,触发AbortError以此判断用户可能拒绝了权限。修改你的代码如下:
autoFillPin() { if (!this.webOtpApiSupported) { this.remoteLog("Web OTP API not Supported"); return; } this.remoteLog( "Web OTP API Supported " + (window.isSecureContext ? "and context is secure" : ", but context is not secure") ); this.abortController = new AbortController(); // 设置30秒超时,超时后中止请求 const timeoutId = setTimeout(() => { this.abortController.abort(); this.remoteLog("Web OTP请求超时(用户可能拒绝了权限)"); }, 30000); navigator.credentials .get({ otp: { transport: ["sms"] }, signal: this.abortController.signal, }) .then((otp) => { clearTimeout(timeoutId); // 获取到PIN后清除超时 if (!otp || !otp.code) { this.remoteLog(`Web OTP API返回无有效验证码`); return; } this.remoteLog(`Web OTP API获取到验证码,长度为${otp.code.length}`); const keyEvent = { isTrusted: true, target: { id: "pinInput", value: otp.code, }, }; this.onPinChanged(keyEvent); }) .catch((err) => { clearTimeout(timeoutId); if (err.name === "AbortError") { this.remoteLog("Web OTP请求中止(大概率用户拒绝或超时)"); } else { this.remoteLog("navigator.credentials.get错误:" + err); } }) .finally(() => { this.abortController = null; }); },
2. 监听页面焦点变化辅助判断
用户点击拒绝按钮后,页面通常会重新获得焦点,可以监听focus事件,结合Promise的状态来辅助判断,但这种方式并非绝对可靠(用户可能只是切换页面后返回),建议和超时逻辑配合使用:
autoFillPin() { if (!this.webOtpApiSupported) { this.remoteLog("Web OTP API not Supported"); return; } this.remoteLog( "Web OTP API Supported " + (window.isSecureContext ? "and context is secure" : ", but context is not secure") ); this.abortController = new AbortController(); const timeoutId = setTimeout(() => { this.abortController.abort(); this.remoteLog("Web OTP请求超时(用户可能拒绝了权限)"); }, 30000); // 页面焦点监听 const handlePageFocus = () => { setTimeout(() => { // 若abortController仍存在,说明Promise未完成,大概率用户拒绝了 if (this.abortController) { this.abortController.abort(); this.remoteLog("用户可能拒绝了Web OTP权限(弹窗关闭后页面获焦)"); window.removeEventListener('focus', handlePageFocus); } }, 500); }; window.addEventListener('focus', handlePageFocus); navigator.credentials .get({ otp: { transport: ["sms"] }, signal: this.abortController.signal, }) .then((otp) => { clearTimeout(timeoutId); window.removeEventListener('focus', handlePageFocus); if (!otp || !otp.code) { this.remoteLog(`Web OTP API返回无有效验证码`); return; } this.remoteLog(`Web OTP API获取到验证码,长度为${otp.code.length}`); const keyEvent = { isTrusted: true, target: { id: "pinInput", value: otp.code, }, }; this.onPinChanged(keyEvent); }) .catch((err) => { clearTimeout(timeoutId); window.removeEventListener('focus', handlePageFocus); if (err.name === "AbortError") { this.remoteLog("Web OTP请求中止(大概率用户拒绝或超时)"); } else { this.remoteLog("navigator.credentials.get错误:" + err); } }) .finally(() => { this.abortController = null; }); },
关键说明
- 超时时间需合理设置,兼顾用户操作时间和页面体验,推荐20-30秒。
- 焦点监听仅作为辅助手段,不能单独用来判定用户拒绝,必须结合超时逻辑。
- 目前WebOTP API规范未提供用户拒绝的直接回调,上述方式是当前可行的解决方案。
内容的提问来源于stack exchange,提问作者Otto Paulsen
相关产品推荐
相关产品推荐

