跳到主要内容

WalletHandlers

WalletHandlers 类是一个高级实用工具,旨在将 DID Connect 工作流无缝集成到 Express.js 或类似的服务器端框架中。它能为给定操作自动设置所有必需的 API 端点,处理您的应用程序与用户的 DID Wallet 之间的通信。

它与 WalletAuthenticator 协同工作,后者负责管理消息的加密签名和验证。由于 WalletHandlers 构建于 Node.js 的 EventEmitter 之上,您可以监听会话状态在令牌存储中变化时的各种生命周期事件。

构造函数

创建 DID Connect 处理程序的实例。该实例负责协调处理 DID Connect 过程中的 HTTP 请求。

DID Connect Handler Setup

javascript
const { WalletHandlers } = require('@did-connect/handler');
const { WalletAuthenticator } = require('@did-connect/authenticator');
const { FileStorage } = require('@did-connect/storage-file');

// 1. 使用您的应用程序的钱包和信息初始化身份验证器
const authenticator = new WalletAuthenticator({ wallet, appInfo, chainInfo });

// 2. 初始化存储适配器以管理会话状态
const tokenStorage = new FileStorage({ dbPath: './sessions.json' });

// 3. 创建处理程序实例
const handlers = new WalletHandlers({
  authenticator,
  tokenStorage,
});

参数

  • config object (required) — WalletHandlers 实例的配置对象。
    • authenticator WalletAuthenticator (required) — 一个负责加密操作的 WalletAuthenticator 实例。
    • tokenStorage object (required) — 一个处理会话状态的令牌存储实例(例如 FileStorageMongoStorage)。
    • pathTransformer function — 一个可选函数,用于转换钱包回调的 URL 路径名,在代理或复杂路由场景中很有用。
    • onConnect function — 一个可选的全局回调函数,在钱包扫描二维码后、身份验证请求发送前执行。它可用于全局权限检查。抛出错误可中止该过程。
    • options object — 一个可选对象,用于自定义处理程序的行为。
      • prefix string (default: /api/did) — 所有生成端点的 URL 前缀。
      • cleanupDelay number (default: 60000) — 从存储中清理已完成会话前等待的时间(毫秒)。
      • tokenKey string (default: _t_) — 会话令牌的查询参数键。
      • encKey string (default: _ek_) — 加密密钥的查询参数键。
      • versionKey string (default: _v_) — 协议版本的查询参数键。

方法

attach(config)

这是 WalletHandlers 类的主要方法。它为特定的用户操作(例如‘登录’、‘支付’)将必要的 DID Connect 路由附加到您的 Express 应用程序。此方法创建一整套端点来处理整个连接生命周期,从令牌生成到钱包响应处理。

附加后,将为给定的 action 创建以下路由:

  • GET {prefix}/{action}/token: 创建一个新的会话令牌。
  • POST {prefix}/{action}/token: 创建一个新的会话令牌。
  • GET {prefix}/{action}/status: 检查会话令牌的状态(例如,等待中、已扫描、已批准)。
  • GET {prefix}/{action}/timeout: 手动使会话令牌过期。
  • GET {prefix}/{action}/auth: 钱包获取身份验证要求的端点。
  • POST {prefix}/{action}/auth: 钱包提交签名响应的端点。

参数

  • config object (required) — 用于附加路由的配置对象。
    • app object (required) — Express 应用程序实例。
    • action string (required) — 此工作流的唯一名称(例如,‘login’、‘claim-asset’)。这将成为 URL 的一部分。
    • onAuth function (required) — 用户在钱包中成功完成流程后执行的回调。您在此处处理已验证的数据。
    • claims object[] — 一个可选的声明数组,用于向用户请求信息(例如,个人资料、签名)。
    • onStart function — 一个可选的回调函数,在新会话启动时执行。
    • onConnect function — 一个可选的回调函数,在钱包扫描二维码时执行。它会为此特定操作覆盖构造函数中的 onConnect
    • onDecline function — 一个可选的回调函数,如果用户在钱包中拒绝请求,则执行该回调。
    • onComplete function — 一个可选的回调函数,在整个工作流完成且会话令牌被销毁后执行。
    • onExpire function — 一个可选的回调函数,用于会话令牌过期时。
    • onError function (default: console.error) — 一个可选的回调函数,用于处理过程中的任何错误。
    • authPrincipal boolean | string (default: true) — 是否首先请求 authPrincipal 声明以确定用户的 DID。
    • persistentDynamicClaims boolean (default: false) — 是否在会话存储中持久化动态生成的声明。

示例

Attach Login Handler

javascript
const express = require('express');
// ... 从构造函数示例中设置身份验证器和令牌存储

const app = express();
const handlers = new WalletHandlers({ authenticator, tokenStorage });

handlers.attach({
  app,
  action: 'login',
  claims: [{ type: 'profile' }], // 请求用户的个人资料
  onAuth: async ({ userDid, claims }) => {
    // 成功登录后的核心逻辑。
    // 使用 userDid 在您的数据库中查找或创建用户。
    const profile = claims.find(x => x.type === 'profile');
    console.log(`用户 ${userDid} 已登录,用户名为: ${profile.fullName}。`);
    // 现在您可以为用户的 Web 会话设置 cookie 或 JWT。
  },
  onComplete: ({ token }) => {
    console.log(`会话 ${token} 已完成并清理。`);
  },
  onError: ({ error }) => {
    console.error('登录流程中发生错误:', error);
  }
});

app.listen(3000, () => {
  console.log('DID Connect 服务器正在端口 3000 上运行');
});

事件

由于 WalletHandlers 继承自 EventEmitter 并监听 tokenStorage 实例,您可以订阅事件来监控会话的生命周期。这对于向用户提供实时反馈特别有用,例如,通过使用 WebSockets 在扫描二维码时更新 UI。

EventPayloadDescription
createdobject当创建新会话令牌时发出。负载包含令牌和初始会话数据。
updatedobject当会话更新时发出。负载包含更新后的会话数据。一个常见的用途是检查 status: 'scanned'
deletedobject当会话令牌被销毁时发出,无论是在完成、过期还是手动超时的情况下。负载包含被移除的令牌。

示例

Listening to Session Events

javascript
// 假设 'io' 是附加到您的 Express 应用程序的 Socket.IO 服务器实例
handlers.on('updated', (payload) => {
  // 通知特定的 Web 客户端二维码已被扫描
  if (payload.status === 'scanned') {
    console.log(`钱包已扫描令牌: ${payload.token}`);
    // 'sid' (socket ID) 应在会话创建时存储在会话中
    if (payload.sid) {
      io.to(payload.sid).emit('wallet_scanned');
    }
  }
});

handlers.on('deleted', ({ token }) => {
  console.log(`令牌 ${token} 已从存储中删除。`);
});