跳到主要内容

处理钱包响应

正如 请求声明 指南所示,在为 DID Connect 会话定义声明后,下一个关键步骤是处理来自用户 DID 钱包的响应。@arcblock/did-connect-js 库提供了一套全面的生命周期回调,用于管理会话的每一种可能结果,无论用户是批准、拒绝还是会话超时。

正如 请求声明 指南所示,在为 DID Connect 会话定义声明后,下一个关键步骤是处理来自用户 DID 钱包的响应。@arcblock/did-connect-js 库提供了一套全面的生命周期回调,用于管理会话的每一种可能结果,无论用户是批准、拒绝还是会话超时。

这些回调作为函数传递给 handlers.attach() 方法,为您提供切入身份验证流程的特定挂钩,以运行您的业务逻辑。

DID Connect 生命周期

一个典型的 DID Connect 会话遵循一个清晰的事件序列。下图说明了此流程,并指出了每个主要生命周期回调的触发时机。

Handling Wallet Responses

生命周期回调

以下是可用于处理整个 DID Connect 会话生命周期的回调的详细说明。

回调触发时机...常见用例
onStart新会话已启动且二维码已准备就绪。记录会话启动,准备会话特定资源。
onConnect用户的钱包扫描二维码并建立连接,但在声明被批准之前。基于 userDid 进行权限检查,生成动态声明。
onDecline用户在钱包中明确拒绝请求。显示取消信息,记录拒绝操作。
onAuth(必需) 用户批准请求并提交所请求的声明。核心业务逻辑: 验证声明、创建用户会话、更新数据库。
onComplete整个 DID Connect 会话已结束,无论成功与否。最终清理任务,移除临时会话数据。
onExpire在用户完成操作前会话超时。通知用户二维码已过期,清理过时会话。
onError会话期间发生意外错误。错误记录和报告。

onConnect:预批准逻辑与动态声明

The onConnect 回调功能强大,因为它允许你在获知用户 DID 但在用户看到完整声明列表之前运行逻辑。这是执行权限检查或根据用户身份动态生成声明的理想时机。

参数: { req, challenge, userDid, userPk, extraParams, updateSession }

示例:使用 onConnect 生成动态声明

javascript
handlers.attach({
  action: 'dynamic-claims',

  // 此函数可以是异步的
  onConnect: ({ userDid }) => {
    // 你可以根据白名单检查用户的 DID
    if (!isUserAllowed(userDid)) {
      throw new Error('你无权访问此服务。');
    }

    // 或者根据用户属性返回一组动态声明
    return {
      profile: () => ({
        fields: ['fullName', 'email'],
        description: '请提供你的姓名和电子邮件以继续',
      }),
    };
  },

  onAuth: async ({ claims, userDid }) => {
    // `claims` 现在包含动态生成的个人资料声明的结果
    console.log('动态声明已由以下用户批准:', userDid, claims);
  },
});

onAuth:处理成功认证

The onAuth 回调是 DID Connect 集成的核心。它是唯一必需的回调,在用户于钱包中成功批准请求时执行。它会接收到用户提交的声明数据,你可以利用这些数据来完成应用程序的逻辑,例如让用户登录或验证资产所有权。

参数: { req, challenge, claims, userDid, userPk, extraParams, updateSession }

示例:在 onAuth 中处理提交的声明

javascript
handlers.attach({
  action: 'profile-login',
  claims: {
    profile: () => ({
      fields: ['fullName', 'email'],
      description: '请提供你的姓名和电子邮件以登录。',
    }),
  },

  onAuth: async ({ userDid, claims }) => {
    try {
      const profileClaim = claims.find((c) => c.type === 'profile');
      if (profileClaim) {
        console.log(`用户 ${userDid} 登录成功`);
        console.log('用户资料:', profileClaim.value);
        // 在这里,你通常会创建一个用户会话,保存到数据库等。
        const user = await findOrCreateUser(userDid, profileClaim.value);
      }
    } catch (err) {
      console.error('登录过程中出错:', err);
    }
  },
});

onDecline:处理用户取消操作

如果用户决定在钱包中取消请求,onDecline 回调就会被触发。这使你能够优雅地处理取消操作,例如,在前端通知用户登录尝试已被取消。

参数: { req, challenge, userDid, userPk, extraParams, updateSession }

示例:处理被拒绝的请求

javascript
handlers.attach({
  action: 'profile-login',
  claims: { /* ... */ },
  onAuth: async ({ userDid, claims }) => { /* ... */ },

  onDecline: ({ userDid }) => {
    console.log(`用户 ${userDid} 拒绝了连接请求。`);
    // 你可以更新会话以通知前端
  },
});

使用 updateSession 持久化数据

包括 onAuth、onConnect 和 onStart 在内的多个回调都会接收一个 updateSession 函数作为参数。这个工具对于将信息从后端逻辑传递回会话状态至关重要,然后前端应用程序可以轮询并检索这些信息。

例如,在 onAuth 中成功登录后,你可以生成一个特定于应用程序的 JWT 并将其保存到会话中。你的前端随后可以检索此令牌来认证后续的 API 请求。

  • 持久化普通的、非敏感信息:await updateSession({ key: 'value' });
  • 持久化敏感信息(将被加密):await updateSession({ key: 'sensitive-value' }, true);

示例:使用 updateSession 将令牌传递给前端

javascript
handlers.attach({
  action: 'profile-login',
  claims: { /* ... */ },
  onAuth: async ({ userDid, claims, updateSession }) => {
    const user = await findOrCreateUser(userDid, claims);
    const appToken = generateAppToken(user);

    // 持久化一个非敏感的会话 ID
    await updateSession({ sessionId: 'some-session-id' });

    // 持久化一个敏感的 JWT,它将在会话存储中被加密
    await updateSession({ token: appToken }, true);

    console.log(`用户 ${userDid} 的会话已更新`);
  },
});

通过有效利用这些生命周期回调,你可以构建稳健且用户友好的身份验证流程。既然你已经了解了如何处理单个会话的响应,就可以开始探索更复杂的用户旅程。

接下来,请在 链接工作流 指南中学习如何通过将多个 DID Connect 会话链接在一起来创建多步骤交互。