参考

DID Connect 协议


DID Connect 协议是一个开放协议,它利用密码学技术,提供了一种安全、去中心化的机制,旨在连接用户与 DApp。在此协议中,我们将 Connect 定义为用户向 DApp 提供所请求声明应答的流程。

本协议涉及以下各方:

  • 用户:指终端用户,他们通常需借助钱包参与工作流程,而由 DID Labs 提供的 DID Wallet 便是一项解决方案。
  • DApp: 为用户提供服务的应用程序。
  • 中继:作为连接 DApp 与用户的桥梁。
  • 注册链:去中心化信任机构。ArcBlock 提供 ABT 链作为去中心化信任机构。

整个连接工作流包含三个环节:

  • 预备知识
  • 请求 DID Connect
  • 响应 DID Connect (可能包含多轮交互)

Image

预备知识#

预知信息指的是用户在正式建立 DApp 连接前,获取关于该 DApp 相关详情的过程。DApp 可通过二维码或深度链接提供此类信息。

以下是二维码或深层链接的内容示例。

https://didWallet.io/i?action=requestAuth&url=https://example-dapp.io/auth/
  • linkPath:此 linkPath 位于链接的开头,用于定位钱包。在此示例中,'linkPath' 为 https://didWallet.io/i 。此部分可配置,SDK 允许开发者为其应用程序注册自己的域名。
    • 当二维码经由第三方相机(例如 iPhone)扫描时,如若已安装 Wallet,系统将自动打开 Wallet 并传递相关参数。若未安装 Wallet,则会跳转至安装页面。用户点击此类链接时,其行为逻辑亦然。上述流程均依托于各平台(如 iOS 和 Android)的深度链接技术。
    • 如果二维码被 Wallet 扫描,Wallet 应该忽略本节并解析参数。
  • action:钱包在下一步应执行的动作。在此,该动作应为 requestAuth,并且钱包应使用 GET 方法访问该 url
  • url: 一个 x-www-form-urlencoded URL。钱包稍后会使用此 URL 来启动 DID Connect 请求流程。

请求 DID Connect#

请求 DID Connect 标志着钱包正式启动连接过程。钱包会向从上一步获取的 url 所指向的端点发起请求。此过程的详细步骤分解如下:

  1. 钱包向该端点发起请求。GET https://example-dapp.io/api/connect/relay
  2. DApp 返回的响应应包含以下字段:
  • appPk: 必需,DApp 的公钥,采用比特币 Base58 编码。
  • agentPk: 可选,已获得 DApp 授权的代理公钥。
  • authInfo: 必需,一个标准的 JWT 令牌。
  • appSession:可选,用于 DApp 处理此连接会话的加密数据。钱包必须原样将其返回给 DApp。此字段的加密方式由 DApp 自行决定。以下是响应负载示例。
{
"appPk": "zBdZEnbDJTijVVCx4Nx68bzDPPMFwVizSRorvzSS3SGG2",
"agentPk": "zNKtCNqYWLYWYW3gWRA1vnRykfCBZYHZvzKr",
"authInfo": "eyJhbGciOiJFZDI1NTE5IiwidHlwIjoiSldUIn0.eyJleHAiOjE1NDg4MDM0MjIsImlhdCI6MTU0ODcwMzQyMiwiaXNzIjoiZGlkOmFidDp6Tkt0Q05xWVdMWVdZVzNnV1JBMXZuUnlrZkNCWllIWnZ6S3IiLCJuYmYiOjE1NDg3MDM0MjIsInJlcXVlc3RlZENsYWltcyI6eyJkb2N1bWVudHMiOlt7Imhhc2giOiJUaGUgaGFzaCBvZiB0aGUgZG9jdW1lbnQncyBjb250ZW50IiwidXJpIjoiaHR0cHM6Ly9kb2N1bWVudC0xLmlvIn0seyJoYXNoIjoiVGhlIGhhc2ggb2YgdGhlIGRvY3VtZW50J3MgY29udGVudCIsInVyaSI6ImlwZnM6Ly9kb2N1bWVudC0yIn1dLCJwcm9maWxlIjpbImZ1bGxOYW1lIiwicGhvbmUiLCJzaGlwcGluZ0FkZHJlc3MiXSwicHJvb2ZPZkhvbGRpbmciOlt7InRva2VuIjoidG9rZW4gbmFtZSAxIiwidmFsdWUiOjE4MDAwMDB9LHsidG9rZW4iOiJ0b2tlbiBuYW1lIDIiLCJ2YWx1ZSI6MTAwMDAwMH1dfSwicmVzcG9uc2VBdXRoVXJpIjoiaHR0cHM6Ly9leGFtcGxlLWFwcGxpY2F0aW9uL3Jlc3BvbnNlLWF1dGgifQ.RasZv6ydSxOBj3H726P8THeo4K4IAd7wapqrdE4hrOVRONByAHYK1kr7uAXASc_-Mw9ShD3IcqAuwnLiEkvHCQ",
"appSession": ""
}

如上所示的 authInfo 的头部和正文部分解码后为

{
"alg": "Ed25519",
"typ": "JWT"
}
{
"iss": "did:abt:zNKtCNqYWLYWYW3gWRA1vnRykfCBZYHZvzKr",
"agentDid": "did:abt:zNKtCNqYWLYWYW3gWRA1vnRykfCBZYHZvzKr",
"iat": 1548703422,
"nbf": 1548703422,
"exp": 1548803422,
"challenge": "F16E730BFB914FA1",
"version": "1.0",
"appInfo": {
"name": "The name of the DApp",
"description": "The description of the DApp.",
"url": "https://example-dapp",
"logo": "https://example-dapp/logo"
},
"chainInfo": {
"host": "https://example-dapp/api"
},
"action": "responseAuth",
"url": "https://example-dapp/api/connect/relay/auth",
"requestedClaims": [
{
"type": "authPrincipal",
"description": "Please select account to continue."
}
],
"verifiableClaims:" [
{
"type": "certificate",
"content": "eyJleHAiOjE1NDg4MDM0MjIsImlhdCI6MTU0ODcwMzQyMiwiaXNzIjoiVGhlIGlzc3VlcidzIGRpZCBvZiB0aGUgY2VydGlmaWNhdGUuIiwibmJmIjoxNTQ4NzAzNDIyLCJvcHMiOnsiYWdyZWVtZW50IjpbImRpZ2VzdCBvZiBhZ3JlZW1lbnQgb25lIiwiZGlnZXN0IG9mIGFncmVlbWVudCB0d28iXSwicHJvZmlsZSI6WyJmdWxsTmFtZSIsIm1vYmlsZVBob25lIiwibWFpbGluZ0FkZHJlc3MiXX0sInBrIjoiVGhlIHBrIG9mIHRoZSBpc3N1ZXIiLCJzdWIiOiJUbyB3aG9tIHRoZSBjZXJ0aWZpY2F0ZSBpcyBpc3N1ZWQuIn0",
"sig": "The signature"
}
]
}
  • iss:此 JWT 有效载荷的签发者。在此示例中,它是通过 appPk 生成的 DApp 的 DID。
  • iat、 nbf 和 exp:遵循 JWT 标准。
  • 版本:DID Auth 协议的版本。
  • agentDid: 如果此负载由经另一个 DApp 授权的代理签名,则此参数应为代理 DID。
  • 挑战码:由DApp生成的一个8字节十六进制随机数,它必须由Wallet原样返回,并在签署认证响应时一并包含;DApp应能据此验证签名和该挑战码的正确性。
  • appInfo:此 DApp 的基本信息,例如名称、描述、徽标等。
  • chainInfo:此链的基本信息。目前,该协议在该字段中仅支持 host
  • url:一个必填字段,Wallet 将在 DID Connect 响应流程中使用此字段。
  • action: 指示 Wallet 下一步应执行的操作。此处应为 responseAuth,且 Wallet 需使用 POST 方法访问 url
  • requestedClaims:指 DApp 所需的可验证声明。在本例中,请求声明的类型是 authPrincipal,这通常是 DApp 在连接过程中要求的首个声明。此声明意味着 DApp 要求钱包先设置认证主体,才能继续进行后续操作。认证主体即用户的 DID,其私钥将用于签署所有从钱包发送至 DApp 的请求。
  • verifiableClaims: DApp所提供的可验证声明。在进行任何后续处理之前,钱包均应验证这些声明。
  1. 收到响应后,DID 钱包应进行以下验证:
    1. 验证 iat 是否在请求发送之后。
    2. 通过使用 exp验证响应是否过期。
    3. 验证签名是否与 appPk 匹配,并核实此 appPk 是否与 iss 字段中的 appDid 相符。
  2. (TBD) 钱包可以(或可应用户要求)向注册区块链查询 DApp 的元数据,例如 trustLevel。ArcBlock 提供 ABT 链作为注册区块链。
  3. (TBD) 钱包在向用户展示所请求的声明时,可以使用 trustLevel。对于在注册链上找不到其 appDid 的DApp,钱包应将整个页面标记为高风险。如果一个DApp请求的可验证声明,其所需的 trust_level 高于该 appDid 所属的 trust_level,则钱包应将这些声明标记为高风险。
  4. 钱包需要确定作为认证主体的 DID。若用户此前已访问过 DApp,则钱包应直接将扩展 DID 用作 authPrincipal。否则,钱包应在执行其他操作前,创建一个 扩展 DID

DID Connect 响应#

这一步可视为,**DID Wallet** 借助 requestedClaims 字段答复 **DApp** 所提出的问题,而 **DApp** 则可能依据这些答复,进一步提出更多问题。

在确定了用于响应 DApp 的 userDid 值后,钱包应按以下格式组织响应:

{
"userPk": "",
"userInfo": "",
"userSession": "",
"appSession": ""
}

  • userPkuserDid 所对应的公钥。
  • userInfo:一个经 JWT 格式签名的认证对象。
  • userSession:DID Wallet 处理此次连接会话所需的加密信息。DApp 必须将此字段原封不动地返回给 DID Wallet。此部分的加密由 DID Wallet 负责。
  • appSession:供 DApp 处理此连接会话的加密信息。钱包必须将其原封不动地返回给 DApp。

就像 authInfo 一样, userInfo 也是一个标准的 JWT 对象,在本例中可按以下方式解码:

{
"alg": "Ed25519",
"typ": "JWT"
}
{
"iss": "userDid",
"iat": "1548713422",
"nbf": "1548713422",
"exp": "1548813422",
"challenge": "F16E730BFB914FA1",
}

字段 iss 表示此 JWT payload 的签发者。同时,它也是 上一步中确定的认证主体

更丰富的 DID 认证响应#

在 DID Wallet 设定认证主体后,DApp 可根据其业务逻辑,向 DID Wallet 发送更多声明。这犹如银行的客户经理在为客户办理业务前,会提出一系列问题。为此,我们来看一个 DApp 在首轮认证后可向 DID Wallet 返回的示例响应。

{
"appPk": "zBdZEnbDJTijVVCx4Nx68bzDPPMFwVizSRorvzSS3SGG2",
"authInfo": "eyJhbGciOiJFZDI1NTE5IiwidHlwIjoiSldUIn0.eyJleHAiOjE1NDg4MDM0MjIsImlhdCI6MTU0ODcwMzQyMiwiaXNzIjoiZGlkOmFidDp6Tkt0Q05xWVdMWVdZVzNnV1JBMXZuUnlrZkNCWllIWnZ6S3IiLCJuYmYiOjE1NDg3MDM0MjIsInJlcXVlc3RlZENsYWltcyI6eyJkb2N1bWVudHMiOlt7Imhhc2giOiJUaGUgaGFzaCBvZiB0aGUgZG9jdW1lbnQncyBjb250ZW50IiwidXJpIjoiaHR0cHM6Ly9kb2N1bWVudC0xLmlvIn0seyJoYXNoIjoiVGhlIGhhc2ggb2YgdGhlIGRvY3VtZW50J3MgY29udGVudCIsInVyaSI6ImlwZnM6Ly9kb2N1bWVudC0yIn1dLCJwcm9maWxlIjpbImZ1bGxOYW1lIiwicGhvbmUiLCJzaGlwcGluZ0FkZHJlc3MiXSwicHJvb2ZPZkhvbGRpbmciOlt7InRva2VuIjoidG9rZW4gbmFtZSAxIiwidmFsdWUiOjE4MDAwMDB9LHsidG9rZW4iOiJ0b2tlbiBuYW1lIDIiLCJ2YWx1ZSI6MTAwMDAwMH1dfSwicmVzcG9uc2VBdXRoVXJpIjoiaHR0cHM6Ly9leGFtcGxlLWFwcGxpY2F0aW9uL3Jlc3BvbnNlLWF1dGgifQ.RasZv6ydSxOBj3H726P8THeo4K4IAd7wapqrdE4hrOVRONByAHYK1kr7uAXASc_-Mw9ShD3IcqAuwnLiEkvHCQ",
"appSession": "",
"userSession": ""
}

上文显示的 authInfo 的头部和主体部分,解码后为

{
"alg": "Ed25519",
"typ": "JWT"
}
{
"iss": "did:abt:zNKtCNqYWLYWYW3gWRA1vnRykfCBZYHZvzKr",
"iat": 1548703422,
"nbf": 1548703422,
"exp": 1548803422,
"version": "1.0",
"challenge": "F16E730BFB914FA1",
"appInfo": {
"name": "The name of the application",
"description": "The description of the application.",
"logo": "https://example-dapp/logo"
},
"action": "responseAuth",
"url": "https://example-app/auth",
"requestedClaims": [
{
"type": "profile",
"description": "Please fill in basic information.",
"items": ["fullName", "mobilePhone", "mailingAddress"]
},
{
"type": "agreement",
"description": "The user data usage agreement.",
"meta": {
"name": "user_agreement",
},
"uri": "https://document-1.io",
"hash": {
"method": "sha2",
"digest": "The hash result of the document's content"
}
},
{
"type": "agreement",
"description": "The service agreement",
"meta": {
"name": "service_agreement"
},
"uri": "ipfs://document-2",
"hash": {
"method": "sha3",
"digest": "The hash result of the document's content"
}
}
]
}

  • meta:作为每个请求声明中的一个可选字段,钱包 必须 将其原样返回给 DApp。
  • description: 向最终用户显示的描述。

因此,在此示例中,DApp 要求钱包提供用户的一些基本信息。钱包将提示用户完成这些信息提供,并将其返回给 DApp。我们将在下一节中讨论如何处理各类信息请求。

终止认证流程#

若要结束认证工作流,你可以按如下方式发送最终响应:

{
"alg": "Ed25519",
"typ": "JWT"
}
{
"iss": "did:abt:zNKtCNqYWLYWYW3gWRA1vnRykfCBZYHZvzKr",
"iat": 1548703422,
"nbf": 1548703422,
"exp": 1548803422,
"version": "1.0",
"challenge": "F16E730BFB914FA1",
"appInfo": {
"name": "The name of the application",
"description": "The description of the application.",
"logo": "https://example-dapp/logo"
},
"status": "ok",
"successMessage": "success message"
"response": {
"key": "value"
}
}

或者你可以返回一条错误消息,例如:

{
"status": "error",
"errorMessage": "error message"
}

将一些数据回传至 Wallet 以实现持久化:

{
"status": "ok",
"response": {
"disposition": "attachment",
"type": "VerifiableCredential",
"data": "json presentation of the credential"
}
}

串联多个工作流#

如果您希望钱包在当前工作流完成后继续处理其他工作流,可以将后续工作流的 deeplink 包含在最终响应中。例如:

{
"alg": "Ed25519",
"typ": "JWT"
}
{
"iss": "did:abt:zNKtCNqYWLYWYW3gWRA1vnRykfCBZYHZvzKr",
"iat": 1548703422,
"nbf": 1548703422,
"exp": 1548803422,
"version": "1.0",
"challenge": "F16E730BFB914FA1",
"appInfo": {
"name": "The name of the application",
"description": "The description of the application.",
"logo": "https://example-dapp/logo"
},
"nextWorkflow": "https://didWallet.io/i?action=requestAuth&url=https://example-dapp.io/next/workflow/"
}

拒绝 DID Connect#

在某些情况下,如果用户选择拒绝来自 DApp 的请求,钱包应将此拒绝情况告知 DApp,以提供更优质的用户体验。具体操作是将 action 字段设置为 declineAuth,并使 requestedClaims 字段保持为空。

{
"iss": "did:abt:zNKtCNqYWLYWYW3gWRA1vnRykfCBZYHZvzKr",
"iat": 1548703422,
"nbf": 1548703422,
"exp": 1548803422,
"version": "1.0",
"action": "declineAuth",
"challenge": "F16E730BFB914FA1",
"requestedClaims": []
}

撤销 DID 认证#

待定

可验证声明#

可验证声明是一份包含多项声明的清单。不同类型的声明,即便设计初衷旨在服务于不同的应用场景,却共享着一些共同属性。

  • type: 该字段是用于标识声明项类型的必填字段。
  • description: 供最终用户显示的消息,为所有类型声明项的必填字段。为节省网络带宽,钱包在返回声明时可省略此字段。
  • meta: 是适用于所有类型声明项的可选字段。DApp 可在其内放置信息,以便轻松处理 DID Wallet 返回的声明。DID Wallet 必须 在每个声明项中原样返回此字段。

支持的声明类型,具体如下:

认证主体#

这是 DApp 首次提出的声明,旨在通知 钱包 为整个身份验证过程设置身份验证主体。该身份验证主体即为 钱包 发送的 JWT 载荷中的 iss 字段。

为了让 DID Wallet 配置认证主体,DApp 应向其响应此声明。通常情况下,authPrincipal 声明会静默完成,用户无需察觉。但 DApp 也可以通过在声明中将 supervised 字段设为 true,来要求 DID Wallet 显示连接界面:

{
"requestedClaims": [
{
"type": "authPrincipal",
"description": "Please set the authentication principal.",
"supervised": true
}
]
}

有时,DApp 可能希望 DID 钱包证明其为特定用户。在这种情况下,DApp 可以在声明项中添加字段 target。DID 钱包必须将认证主体设置为该特定 DID。

{
"requestedClaims": [
{
"type": "authPrincipal",
"description": "Please set the authentication principal to start this atomic swap.",
"target": "did:abt:z1fw9Ycbb7cJnj1NUm6hyuSYHuTHEwph8yH",
"supervised": false
}
]
}

有时 DApp 可能希望钱包生成一个带有自定义参数的 DID,DApp 可以在声明项中添加以下字段:

{
"requestedClaims": [
{
"type": "authPrincipal",
"description": "Please generate an application account with the following settings",
"declareParams": {
"moniker": "application_moniker",
"issuer": "z1fw9Ycbb7cJnj1NUm6hyuSYHuTHEwph8yH"
},
"targetType": {
"role": "application",
"hash": "sha3",
"key": "ed25519"
},
"supervised": false
}
]
}

默认情况下,所有 DID 均以 base58_btc 格式编码。

钱包将根据 targetType 设置生成一个新的 DID,并确保在回应声明之前,该 DID 已通过 `declareParams` 完成声明。钱包仅需随机生成该 DID 的密钥对。

简介#

个人资料作为一种可验证声明,用于收集用户的姓名、电子邮件、年龄等基本信息。所请求的具体信息被定义在 items 子字段中。例如,当 DApp 需要用户提供其名、手机号码和邮寄地址时,可以采用以下方式向 Wallet 发出一个声明。

{
"requestedClaims": [
{
"type": "profile",
"description": "Please provide the basic information.",
"items": ["fullName", "mobilePhone", "mailingAddress"]
}
]
}

钱包应通过以下方式回应此请求:

{
"requestedClaims": [
{
"type": "profile",
"fullName": "Alice Bean",
"mobilePhone": "123456789",
"mailingAddress": {
"addressLine1": "456 123th AVE",
"addressLine2": "Apt 106",
"city": "Redmond",
"state": "WA",
"postalCode": "98052",
"country": "USA"
}
}
]
}

请参阅 附录,查看完整的资料项列表。

协议#

协议是另一种常用的声明类型。它代表着 DApp 要求用户签署的协议。一个 agreement 声明类型包含以下字段:

  • uri: URI 用于指定协议内容。
  • hash: 一个对象,其 method 子字段用于指定所使用的算法(如 sha3、sha2 等),而 digest 子字段则为哈希值。
  • agreed: 由 DID Wallet 添加的布尔值,用于表明用户是否同意该协议。
  • sighash 的 DSA 签名。

DApp 若需用户签署协议,应在响应中包含此类声明项列表。

{
"requestedClaims": [
{
"type": "agreement",
"description": "The user data usage agreement.",
"meta": {
"name": "user_agreement."
},
"uri": "https://document-1.io",
"method": "sha2",
"digest": "The hash result of the document's content"
},
{
"type": "agreement",
"description": "The service agreement",
"meta": {
"name": "service_agreement"
},
"uri": "ipfs://document-2",
"method": "sha3",
"digest": "The hash result of the document's content"
}
]
}

当收到此响应时,DID Wallet 应提示用户签署协议。随后,DID Wallet 应将已签署的声明项列表提交回DApp。如果用户同意,DID Wallet 应添加 agreed 字段和 sig 字段。如果用户拒绝,则 DID Wallet 只需添加值为 false 的 agreed 字段。此情况下无需签名。

{
"requestedClaims": [
{
"type": "agreement",
"uri": "https://document-1.io",
"method": "sha2",
"digest": "The hash result of the document's content",
"meta": {
"name": "user_agreement."
},
"agreed": true,
"sig": "user's signature against the doc digest plus AGREED."
},
{
"type": "agreement",
"uri": "ipfs://document-2",
"method": "sha3",
"digest": "The hash result of the document's content",
"meta": {
"name": "service_agreement"
},
"agreed": false
}
]
}

资产#

资产声明项旨在让钱包向 DApp 提供一个链上 Asset DID。通常,DApp 需结合签名和链上资产状态,对资产所有权进行核验。因此,若需钱包提供 Asset DID,DApp 应当遵循以下方式发送声明:

  • filters:必填字段,用于指定 Wallet 选择资产所依据的筛选条件列表。若指定了多个筛选条件,它们之间应视为逻辑 OR 关系;同一筛选条件内的多个字段,则应视作逻辑 AND 关系;一个字段中的多个值,则应视作逻辑 OR 关系。每个筛选条件可包含以下任一参数:
    • trustedIssuers: 受信任发行者的 DID 列表,默认为空列表
    • trustedParents:受信任的父 DID 列表,通常指 NFT 工厂地址,默认为空列表。
    • tag: 资产标签,用于筛选,默认为空
    • address: 资产的 DID,无论是链上还是链下;默认为空字符串。
  • optional:此字段指示用户是否可以跳过此声明。若 optional 为 true,则声明响应可以为空。该 optional 字段默认为 false。
{
"requestedClaims": [
{
"type": "asset",
"description": "Please provide the coupon you own.",
"optional": false,
"filters": [
{
"address": "zje1uzZTCZN551EWGLyyCEW9AM2wAdjymfHb"
},
{
"trustedIssuers": ["zje1uzZTCZN551EWGLyyCEW9AM2wAdjymfHb"],
"tag": "abcd"
},
{
"trustedParents": ["zje1uzZTCZN551EWGLyyCEW9AM2wAdjymfHb"],
"tag": "abcd"
},
{
"trustedIssuers": ["zje1uzZTCZN551EWGLyyCEW9AM2wAdjymfHb"],
"trustedParents": ["zje1uzZTCZN551EWGLyyCEW9AM2wAdjymfHb"],
"tag": "abcd"
}
],
"meta": {}
}
]
}

钱包应以如下方式返回响应:

{
"requestedClaims": [
{
"type": "Asset",
"asset": "did:abt:zje1uzZTCZN551EWGLyyCEW9AM2wAdjymfHb",
"ownerDid": "NFTOwner.address",
"ownerPk": "toBase58(NFTOwner.publicKey)",
"ownerProof": "toBase58(NFTOwner.sign(challenge))",
"meta": {}
}
]
}
  • asset 指的是用户所持有的链上资产地址,该用户可以是同一钱包内的任意有效用户。
  • ownerDid 指的是链上资产所有者的 DID,其应始终包含在内,因为链上资产所有者有时可能与会话 `userDid` 不同。
  • ownerPk 是链上资产所有者的公钥,该字段应始终被包含,因为在某些情况下所有者的公钥无法通过其他途径获取。
  • ownerProof 是链上资产所有者针对会话挑战所作的签名。

签名#

签名是 **DID Wallet** 与 DApp 之间一种常见的验证凭证。当 DApp 需要引导 **DID Wallet** 用户完成某项业务操作时,通常最终会涉及对交易进行签名并将其广播到区块链。在这种情况下,DApp 通常会组装一笔交易,并发送给用户请求签名。DApp 应该将交易的原始完整负载和类型 URL 发送至 **DID Wallet**,以便 **DID Wallet** 能够验证哈希并向用户显示交易详情。

除所有声明项共有的通用字段外,签名声明项还包含以下额外字段:

  • typeUrl:该字段指明了此交易的类型 URL,有助于 Wallet 解码并显示原始交易。
  • origin: 交易的 Base58 编码有效载荷。此有效载荷需通过 typeUrl 进行解码。
  • method:用于生成 origin 消息摘要的哈希方法。此值应与用户 DID 中的哈希方法一致。
  • digestorigin 的消息摘要。此为钱包签名的数据。钱包将验证此摘要是否与原始数据匹配。
  • display: 交易执行后用户将获得结果的视觉呈现。
  • sig: 待返回的签名。
{
"requestedClaims": [
{
"type": "signature",
"description": "Please sign this exchange transaction.",
"typeUrl": "fg:t:transaction",
"origin": "base58 encoded data",
"method": "sha3",
"display": "JSON stringified display info",
"digest": "base58 encoded digest",
"meta": {
"id": 12345
}
}
]
}

钱包应以如下格式返回签名:

{
"requestedClaims": [
{
"type": "signature",
"typeUrl": "fg:t:transaction",
"origin": "base58 encoded data",
"method": "sha3",
"digest": "base58 encoded digest",
"meta": {
"id": 12345
},
"sig": "the value to return"
}
]
}

已知 typeUrl 列表:

  • fg:t:transaction:需要 Wallet 将原始数据解码为交易并签名。
  • mime:text/plain: 钱包需要将原始内容解码为文本并展示给用户。
  • mime:text/html:钱包需将原始数据解码成 HTML 内容并展示给用户。
  • eth:transaction: 钱包需将原始数据解码为 JSON 字符串,其格式如下:{
    "network": 1, // 以太坊网络 ID
    "tx": {
    "to": "<eth address>",
    "value": "<number string>", // 例如:"10000000000000000" 等价于 0.1 ETH
    "gasLimit": "<number string>", // 例如:25100, 80000等
    "data": "xxxx" // 如果这不是合约调用,请传入空字符串
    }
    }
    钱包将利用此 JSON 数据生成并发送一笔以太坊(ETH)交易。交易发送前,钱包须向用户展示交易详情,并要求用户输入密码进行二次确认,方可最终发送。

准备交易#

prepareTx 通常用于 DApp 需要 Wallet 支付特定数量的通证以完成某个业务流程的场景。在这种情况下,DApp 知晓应发送何种交易,但尚不清楚组装交易所需的某些参数。DApp 会首先组装一个部分交易,并附带支付要求,将其发送给 Wallet。Wallet 收到后,会确定应使用哪些账户支付该交易,然后组装完整的交易,以便在界面上展示并等待用户签名。经用户确认后,Wallet 会将包含签名的完整交易提交给 DApp 进行广播。

除了各类声明项的共有字段外,prepareTx 声明项还包含以下额外字段:

  • partialTx: 部分交易的 Base58 编码结果。提交时,钱包不得修改该编码结果。
  • 要求: 该交易的支付要求。钱包在提交时应保持其不变。
    • requirement.tokens:这是一个列表,用于指定各代币地址所需的数量。对于主代币,其代币地址应留空。如果该列表被设置为空数组,则表示此交易不需要任何代币。
    • requirement.assets: 是一个供 DID Wallet 选取资产列表的对象,支持通过地址或父地址进行筛选。当设置为空对象时,表示该交易无需任何资产。
  • finalTx:最终签名交易的 Base58 编码结果,无需任何进一步操作即可广播到区块链。钱包可以更改以下字段以创建最终交易:
    • tx.signatures 和 tx.signature 和 tx.itx.inputs:如果交易需要由多个账户支付
    • tx.from 和 tx.delegator:若交易需经委托完成
  • display: 对交易执行后用户所得内容的视觉呈现。
{
"requestedClaims": [
{
"type": "prepareTx",
"description": "Please pay 2 ABT to buy VIP",
"partialTx": "base58 encoded partial tx",
"display": "JSON stringified display info",
"requirement": {
"tokens": [
{
"address": "TOKEN_ADDRESS",
"value": "TOKEN_AMOUNT_AS_BIG_NUMBER_STRING"
}
],
"assets": {
"address": ["LIST_OF_SPECIFIC_ASSETS"],
"parent": ["LIST_OF_ASSET_PARENTS"],
"amount": 2
}
},
"meta": {
"orderId": 12345
}
}
]
}

钱包将以如下格式返回最终交易:

{
"requestedClaims": [
{
"type": "prepareTx",
"partialTx": "base58 encoded partial tx",
"finalTx": "base58 encoded final tx",
"requirement": {
"tokens": [
{
"address": "TOKEN_ADDRESS",
"value": "TOKEN_AMOUNT_AS_BIG_NUMBER_STRING"
}
],
"assets": {
"address": ["LIST_OF_SPECIFIC_ASSETS"],
"parent": ["LIST_OF_ASSET_PARENTS"],
"amount": 2
}
},
"meta": {
"orderId": 12345
}
}
]
}

证书#

证书的核心是由颁发者签名的 JWT 令牌。

{
"type": "certificate",
"content": "eyJhbGciOiJFZDI1NTE5IiwidHlwZSI6IkpXVCJ9.eyJhZ2VudERpZCI6ImRpZDphYnQ6ek5LcnJQSHdCbnlWb01WWjZaRnRwcW5MR3NDUVh3aXNFdTZqIiwiZXhwIjoxNjA1MDg3MjQyLCJpYXQiOjE1NzM1NTEyNDIsImlzcyI6ImRpZDphYnQ6ek5LZzVweGNaUExvZFJBYTVZcGJoQTRtTWZIdzlRRHFyNjVzIiwibmJmIjoxNTczNTUxMjQyLCJvcHMiOnsicHJvZmlsZSI6WyJmdWxsTmFtZSIsIm1vYmlsZVBob25lIiwibWFpbGluZ0FkZHJlc3MiXX0sInZlcnNpb24iOiIxLjAifQ.3O08a7Dgqlebg-2Pu6eZQ2hOsUdjqCu6yE7DsDKUXI18D2bf6hWOpiGvT2wkIAZyvFdiiD0kQm1PgKXcXLTeBw"
}

令牌负载如下:

{
"iss": "The issuer's did of the certificate.",
"pk": "The pk of the issuer",
"agentDid": "To whom the certificate is issued.",
"iat": 1548703422,
"nbf": 1548703422,
"exp": 1548803422,
"ops": {
"profile": ["fullName", "mobilePhone", "mailingAddress"],
"agreement": ["digest of agreement one", "digest of agreement two"]
}
}

可验证凭证#

DApp 使用此声明来验证钱包是否持有所需的有效可验证凭证。

  • filters 是必填字段,用于指定筛选器列表,以供 Wallet 选择可验证凭证。若指定多个筛选器,它们之间应按 OR 逻辑进行解释;而单个筛选器内的多个字段,则应按 AND 逻辑进行解释;单个字段内的多个值,则应按 OR 逻辑进行解释。每个筛选器可包含以下任一参数:
    • trustedIssuers: 受信任发行者的 DID 列表,默认为空列表
    • type:有效可验证凭证类型列表,默认为空列表
    • tag: 可验证凭证标签,默认为空
    • target: 可验证凭证的 DID,可以是链上或链下,默认为空字符串
  • optional 字段允许用户跳过此声明。如果 optional 设为 true,则声明响应可为空。该 optional 字段默认值为 false。
{
"requestedClaims": [
{
"type": "verifiableCredential",
"description": "Please provide the coupon you own.",
"optional": false,
"filters": [
{
"trustedIssuers": ["did:abt:z1RuF9Xa2AUTJ3VHZKy2xroKzphwWKsLrY6"],
"type": ["ServerOwnershipNFT"],
"tag": "extra tag to filter credential"
},
{
"trustedIssuers": ["did:abt:z1RuF9Xa2AUTJ3VHZKy2xroKzphwWKsLrY6"],
"type": ["BlockletServerPassport"]
},
{
"trustedIssuers": ["did:abt:z1RuF9Xa2AUTJ3VHZKy2xroKzphwWKsLrY6"],
"type": ["BlockletServerPassport"]
},
{
"target": "did:abt:z1RuF9Xa2AUTJ3VH"
}
],
"meta": {}
}
]
}

返回

{
"requestedClaims": [
{
"type": "verifiableCredential",
"presentation": "xxxx",
"assetDid": [""]
}
]
}

什么是演示

{
"@context": ["https://achema.arcblock.io/v0.1/context.jsonld"],
"challenge": "F16E730BFB914FA1",
"type": ["VerifiablePresentation"],
"verifiableCredential": [{}],
"proof": [{}]
}

证明的生成过程与可验证凭证相同。一次呈现可以包含多个VC,如果VC来自不同的DID,则该呈现必须提供多个证明。挑战则来自于DID Auth JWT。

证明的结构是

{
"type": proofTypes[typeInfo.pk],
"created": new Date().toISOString(),
"proofPurpose": "assertionMethod",
"pk": "vc recipience's PK base58",
"jws": toBase64(signature)
}

签名由 VC 持有者的私钥签署。

使用场景#

DID Connect 协议是一种通用的点对点协议,可用于任何需要身份验证的场景。它可用于但不限于以下场景:

  • 用户注册。
  • 用户登录。
  • 签署文件。
  • 申请/签发证书。
  • 申请签证。
  • 点对点信息交换。