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

如何在WebAuthn登录流程中正确使用counter计数器

WebAuthn Passkey 登录流程中 Counter 值不递增的问题解决

核心问题定位

你在登录流程里犯了一个关键错误:误用了注册阶段的 attestationOptions() 来生成登录请求选项,这直接导致认证器没有触发 counter 递增逻辑。登录阶段必须使用断言(Assertion)相关的方法,而非注册(Attestation)方法。

正确流程与代码修正

1. 生成登录请求选项(替换错误的 attestationOptions)

登录时要调用 assertionOptions() 生成断言请求,而非注册阶段的 attestationOptions(),示例代码:

// 登录流程:生成断言请求选项
const assertionOptions = await f2l.assertionOptions({
  allowCredentials: [
    {
      type: "public-key",
      id: passkey.credId, // 传入你存储的凭证ID(需转为Uint8Array)
      transports: passkey.transports // 可选,传入存储的凭证传输方式
    }
  ],
  userVerification: "required" // 和注册时的配置保持一致或按需调整
});

// 将生成的challenge存储到服务端,后续验证断言时要用
storedChallenge = assertionOptions.challenge;

2. 正确获取验证后的 Counter 值

Counter 不在 clientData 中,而是存储在认证器返回的 authnrData 里,修正获取方式:

const assertionExpectations = {
  challenge: storedChallenge,
  origin: 'http://localhost:4200', 
  factor: 'either',
  publicKey: passkey.publicKey, // 存储的公钥
  prevCounter: passkey.counter, // 存储的上一次counter值
  userHandle: new TextEncoder().encode(user.email), // 存储的用户ID
};

const authResult = await this.f2l.assertionResult(assertionResponse, assertionExpectations);
// 从authnrData中获取递增后的counter
const newCounter = authResult.authnrData.get('counter');
console.log(newCounter); // 现在会输出递增后的值,比如1、2等

3. Counter 机制的正确运作逻辑

  • 注册阶段:首次注册成功后,认证器会返回初始 counter(通常为0),你需要把这个值和公钥、凭证ID等一起持久化存储到服务端。
  • 登录阶段:
    • 服务端生成断言选项时不需要传入 counter,counter 是认证器内部维护的状态,由认证器负责递增。
    • 验证断言时,必须传入 prevCounter(即之前存储的旧counter值),Fido2Lib 会自动检查返回的新counter是否大于旧值,以此防止重放攻击。
    • 验证成功后,务必将服务端存储的counter更新为新返回的值,供下次登录验证使用。

内容的提问来源于stack exchange,提问作者Jeanluca Scaljeri

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 17:38:25