Skip to content

flunGit/webauthn-server

Repository files navigation

@flun/webauthn-server

WebAuthn npm (scoped)

@flun/webauthn-server 是一个专为 Node.js 环境设计的 WebAuthn(含 Passkeys)服务端工具库,采用 TypeScript 编写,提供对证书、密钥及 CBOR 编码的完整处理能力。它简化了 FIDO2/Passkey 身份验证后端的构建过程,支持当前所有主流的 WebAuthn 认证格式。

模块规范:本包使用 ESM 模块编写。要求 Node.js ≥ 22.12.0,若使用低版本 Node 则需通过 import 语法导入(不建议在低于 22.12.0 的环境中使用 require())。

目录


安装

Node.js 22.12.0 及以上版本

npm install @flun/webauthn-server

注意:本库依赖 Node.js 内置的 cryptofetch 等模块,请确保运行环境为 Node.js ≥ 22.12.0。


主要功能

  • WebAuthn 注册与认证 提供 generateRegistrationOptions / verifyRegistrationResponsegenerateAuthenticationOptions / verifyAuthenticationResponse 两对核心方法,完整实现 WebAuthn 后端逻辑。

  • 全格式证明支持 支持 packedfido-u2fandroid-safetynetandroid-keytpmapplenone 等所有标准认证格式。

  • 元数据服务 (MDS) 内置 MetadataService 可下载并解析 FIDO 联盟的元数据 BLOB,验证认证器真伪与状态。

  • 证书链验证 提供证书路径验证、吊销检查、X.509 解析等工具。

  • 丰富的辅助工具 通过 @flun/webauthn-server/helpers 子路径导出大量底层工具:Base64URL 编解码、COSE ↔ PKCS 转换、authData 解析、签名验证等。


快速开始

后端集成示例

以下是一个基于 Express 的完整后端示例,展示了用户注册与登录的 WebAuthn 流程。

import express from 'express';
import {
  generateRegistrationOptions, verifyRegistrationResponse, generateAuthenticationOptions, verifyAuthenticationResponse
} from '@flun/webauthn-server';

const app = express();
app.use(express.json());

// 模拟用户数据库
const users = {
  'user@example.com': {
    id: Buffer.from('user-id-buffer'),
    credentials: []
  }
};

// 临时挑战存储(生产环境建议使用 session 或 redis)
const challengeStore = new Map();

// 1. 注册 - 生成选项
app.post('/api/register/begin', async (req, res) => {
  const { username } = req.body;
  const user = users[username];
  if (!user) return res.status(404).json({ error: 'User not found' });

  const options = await generateRegistrationOptions({
    rpName: 'My App',
    rpID: 'localhost',
    userName: username,
    userID: user.id,
    attestationType: 'none',
  });

  challengeStore.set(username + '_reg', options.challenge);
  res.json(options);
});

// 2. 注册 - 验证响应
app.post('/api/register/complete', async (req, res) => {
  const { username, response } = req.body;
  const expectedChallenge = challengeStore.get(username + '_reg');

  const verification = await verifyRegistrationResponse({
    response,
    expectedChallenge,
    expectedOrigin: 'http://localhost:3000',
    expectedRPID: 'localhost',
    requireUserVerification: true,
  });

  if (verification.verified) {
    const { credential } = verification.registrationInfo;
    users[username].credentials.push({
      id: credential.id,
      publicKey: credential.publicKey,
      counter: credential.counter,
      transports: credential.transports
    });
    challengeStore.delete(username + '_reg');
    res.json({ verified: true });
  } else {
    res.status(400).json({ verified: false });
  }
});

// 3. 认证 - 生成选项
app.post('/api/login/begin', async (req, res) => {
  const { username } = req.body;
  const user = users[username];
  if (!user) return res.status(404).json({ error: 'User not found' });

  const options = await generateAuthenticationOptions({
    rpID: 'localhost',
    allowCredentials: user.credentials.map(cred => ({
      id: cred.id,
      type: 'public-key',
      transports: cred.transports,
    })),
  });

  challengeStore.set(username + '_auth', options.challenge);
  res.json(options);
});

// 4. 认证 - 验证响应
app.post('/api/login/complete', async (req, res) => {
  const { username, response } = req.body;
  const expectedChallenge = challengeStore.get(username + '_auth');
  const user = users[username];
  const credential = user.credentials.find(c => c.id === response.id);

  const verification = await verifyAuthenticationResponse({
    response,
    expectedChallenge,
    expectedOrigin: 'http://localhost:3000',
    expectedRPID: 'localhost',
    credential,
  });

  if (verification.verified) {
    credential.counter = verification.authenticationInfo.newCounter;
    challengeStore.delete(username + '_auth');
    res.json({ verified: true });
  } else {
    res.status(400).json({ verified: false });
  }
});

app.listen(3001, () => console.log('Backend running on port 3001'));

前端配合说明

前端需使用配套的浏览器库 @flun/webauthn-browser,可通过 CDN 或 npm 引入。

<script src="https://unpkg.com/@flun/webauthn-browser/dist/index.js"></script>

调用示例:

// 注册
const regOptions = await fetch('/api/register/begin', ...).then(r => r.json());
const regResponse = await flunWebAuthnBrowser.startRegistration({ optionsJSON: regOptions });
await fetch('/api/register/complete', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ username, response: regResponse })
});

// 登录
const authOptions = await fetch('/api/login/begin', ...).then(r => r.json());
const authResponse = await flunWebAuthnBrowser.startAuthentication({ optionsJSON: authOptions });
await fetch('/api/login/complete', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ username, response: authResponse })
});

API 参考

注册相关

方法 描述
generateRegistrationOptions(options) 生成用于 navigator.credentials.create() 的选项参数
verifyRegistrationResponse(options) 验证客户端返回的注册响应,返回验证结果及凭证信息

认证相关

方法 描述
generateAuthenticationOptions(options) 生成用于 navigator.credentials.get() 的选项参数
verifyAuthenticationResponse(options) 验证客户端返回的认证响应,返回验证结果及更新的计数器

元数据与设置服务

导出项 描述
MetadataService 协调 FIDO 元数据交互的基础服务实例(BaseMetadataService 的实例)
SettingsService 管理各类 attestation 声明格式根证书的服务实例
supportedCOSEAlgorithmIdentifiers 支持的加密算法标识符数组
AlgSign 支持的签名算法数组

辅助工具函数(helpers 模块)

可通过 @flun/webauthn-server/helpers 导入所有底层工具。

编解码与转换

函数 描述
fromBuffer(buffer) 将 ArrayBuffer 编码为 Base64URL 字符串
toBuffer(base64url) 将 Base64URL 字符串解码为 ArrayBuffer
utf8Tob64url(str) UTF-8 字符串 → Base64URL
b64urlToUtf8(str) Base64URL → UTF-8 字符串
toBase64(base64url) Base64URL → 标准 Base64
isBase64(str) 检查是否为 Base64 编码
isBase64URL(str) 检查是否为 Base64URL 编码
trimPadding(str) 移除 Base64URL 填充字符
fromHex(hex) 十六进制字符串 → Uint8Array
toHex(buf) Uint8Array → 十六进制字符串
utf8Tobytes(str) UTF-8 → Uint8Array
bytesToUtf8(buf) Uint8Array → UTF-8
asciiToBytes(str) ASCII → Uint8Array
areEqual(a, b) 比较两个 Uint8Array 是否相等
concat(...arrays) 拼接多个 Uint8Array
toDataView(buf) 转换为 DataView 对象

COSE 公钥处理

类型/函数 描述
COSEKEYS, COSEKTY, COSECRV, COSEALG COSE 标准键值枚举
COSEPublicKey, COSEPublicKeyOKP, COSEPublicKeyEC2, COSEPublicKeyRSA COSE 公钥类型
isCOSEPublicKeyOKP(), isCOSEPublicKeyEC2(), isCOSEPublicKeyRSA() 类型守卫
isCOSEKty(), isCOSECrv(), isCOSEAlg() COSE 参数值检查
decodeCredentialPublicKey(buf) 解码 CBOR 编码的凭证公钥为 Map 对象
convertCOSEtoPKCS(coseKey) 将 COSE 公钥转换为 PKCS 格式
convertX509PublicKeyToCOSE(certDer) 从 X.509 证书提取公钥并转换为 COSE 结构

证书处理

函数/类 描述
convertCertBufferToPEM(buf) 将证书缓冲区转换为 PEM 字符串
convertPEMToBytes(pem) 将 PEM 证书转换为字节数组
getCertificateInfo(pem) 提取 PEM 证书的颁发者、主题、有效期等信息
isCertRevoked(certPem) 检查证书是否被其 CRL 吊销
validateCertificatePath(certs) 验证证书链的有效性
validateExtFIDOGenCEAAGUID(cert, aaguid) 校验证书中的 FIDO Gen CE AAGUID 扩展
InvalidSubjectAndIssuer 证书链验证失败时抛出的错误类

认证器数据解析

函数/类型 描述
decodeAttestationObject(buf) 解析 attestationObject 缓冲区
decodeClientDataJSON(base64url) 解析 clientDataJSON
parseAuthenticatorData(authData) 解析 authData,返回可读结构
parseBackupFlags(flags) 解析备份状态标志 (BE/BS)
decodeAuthenticatorExtensions(buf) 解码扩展数据
InvalidBackupFlags 备份标志无效时抛出的错误类

签名验证与哈希

函数 描述
verifySignature({ signature, data, publicKey }) 通用签名验证入口
verifyEC2(), verifyOKP(), verifyRSA() 特定算法签名验证
toHash(data, algorithm?) 计算哈希摘要(默认 SHA-256)
mapX509SignatureAlgToCOSEAlg(oid) X.509 签名算法 OID → COSE 算法 ID

通用工具

函数/类 描述
generateChallenge() 生成随机挑战值
generateUserID() 生成随机用户 ID
matchExpectedRPID(rpIdHash, expectedRPIDs) 匹配 RP ID 哈希值
UnexpectedRPIDHash RP ID 不匹配时抛出的错误类
fetch(url, options?) 跨运行时的 fetch 封装
getLogger(name?) 获取基于 debug 的日志记录器
convertAAGUIDToString(aaguid) 将 AAGUID 缓冲区转换为 UUID 字符串
digest(algorithm, data) 生成数据摘要
getRandomValues(arr) 填充随机字节
verify(algorithm, key, signature, data) 底层验签函数

支持的认证格式

本库完全兼容 W3C WebAuthn 规范 中定义的所有证明格式:

  • Packed
  • FIDO U2F
  • Android SafetyNet
  • Android Key
  • TPM
  • Apple
  • None

每种格式的验证逻辑均已内置,无需额外配置。


错误处理

库中大部分验证方法会在失败时抛出明确的错误类,例如:

  • UnexpectedRPIDHash:RP ID 哈希不匹配
  • InvalidSubjectAndIssuer:证书链无效
  • InvalidBackupFlags:备份标志组合非法

建议使用 try...catch 捕获这些错误并作出相应处理。


许可证

ISC © flun


相关链接

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors