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

如何检测用户在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 13:36:02