出示 NFT 之后,服务如何决定是否放行

钱包里有一个 NFT,应用就允许访问。这句话适合演示,拿来描述一个真实服务,还少了最重要的部分:这个请求,凭什么可以被执行?
NFT 可以表示存储使用资格,也可以表示某项服务的访问权。资源服务需要把这种表示翻译成具体决定:这个人现在能不能向这个空间上传文件?能不能修改这个域名?一次成功的出示,只是决定所需的证据进入了系统。
ArcBlock 的 DID Wallet 和 DID Connect 文档提供了一个很好的入口。沿着这个实际交互往下看,NFT 如何连接到资源就清楚了。技术上最有分量的地方,在用户滑动确认之后。
钱包出示了什么
DID Wallet 的 NFT/Passport 使用文档描述的操作很简单:应用请求一项 NFT,钱包显示符合条件的对象,用户选择并滑动确认。文档还专门说明,这个操作不会把 NFT 发送给应用。
这里的“发送”指资产转移。应用仍然需要收到用于核验的信息。把两件事分开,用户才能理解自己同意的是什么:向应用证明一项资格,不等于交出这项资产。
DID Connect 的 Request NFT 示例展示了应用侧怎样发起请求。请求中可以指定对象类型和可信发行人;响应处理里可以看到证明检查,以及后续的资产核验调用。钱包选择界面与服务端判断,在这个例子里是分开的步骤。
这正是 ArcBlock 把 NFT 连接到资源的基本分工:钱包帮助用户选择并出示证据,DID 提供身份基础,VC 表达权利声明,资源服务结合当前资产状态,判断证据是否满足自己要执行的操作。
证明是谁,还要证明是在回应这次请求
仅仅收到一个 NFT 标识没有意义。任何人都可能复制这个标识,凭证文件也可能被复制。服务需要确认,回应请求的一方确实能提供相应的控制证明。
DID 为识别请求者和验证相关密钥提供了基础。DID 文档中的认证用途与其他验证用途有区分,验证者需要按照所采用的方法和协议处理它们。[1] 具体到一次交互,证明还应绑定本次请求及目标应用,并具备防重放安排。否则,截获的一次有效回应可能被拿去重复使用。
公开的 DID Connect 示例会检查证明时间,并验证包含应用地址的签名消息。这让证明与目标应用及时间关联起来。完整的请求协议还需要处理挑战值的生命周期和重放问题。
还有一处常被省略的区别:凭证的 subject 是被描述的对象,holder 是持有并出示凭证的一方,NFT 的当前 owner 则由相应资产系统记录。这几个角色可能重合,也可能不同。[2] 例如,一份凭证描述的是一个存储资源,出示者是用户;服务要核验的是用户与资源之间的授权关系,不能把它们当成同一个身份。
能证明“这是我发来的请求”,也还没有回答“我有权做什么”。
谁签发的,签发了什么权利
签名有效,说明相关内容通过了对应的密码学验证。服务仍要决定,是否接受这个发行人对这类资源作出的声明。
应用不能因为收到一份自己签给自己的“管理员凭证”,就让请求者成为管理员。可信发行人的范围需要有业务依据。DID Connect 请求里的 trustedIssuers 展示了表达这类条件的入口;钱包按条件筛选,有助于减少错误选择,服务端仍要核验实际收到的证明。
接下来才轮到权利内容。
假设一个 NFT 对应某个存储空间的使用资格。允许读取这个空间,不代表允许删除其中的文件;能使用空间,也不自然包含修改账单或交接管理权。服务需要把凭证里的资源标识和允许的操作,与正在处理的请求对应起来。这就是 scope 的实际含义:权利要小到能判断一个具体动作。
“有 NFT”是很粗的条件。“有被接受的发行人签发、对应这个资源、允许这项操作的有效权利”,才足以参与授权决定。
W3C 的 VC 数据模型也明确指出,VC 需要与授权框架结合使用。[3] 可验证声明为决定提供依据,业务规则定义这份依据如何生效。这里没有一条通用标准能替所有服务决定,拥有某个对象就该得到哪些权限。
图中列出一次决定依赖的检查;实际协议可以组合或调整检查顺序。
发行时成立的关系,现在还成立吗
一份签名声明可以保存很久,资源关系却会改变。如果服务承诺“当前持有者可以使用”,就必须有办法获得足够新的持有状态。
用户昨天持有一项 NFT,今天可能已经转移。昨天缓存的核验结果,就需要按服务的规则重新判断。资产是否已消耗、凭证是否在有效期内,也可能影响结果。凭证采用了状态机制时,验证方还需要按该机制处理暂停或撤销等状态;这些状态与签名是否有效是不同的判断。[4]
NFT 转移还会暴露一个更实际的问题:旧用户已经登录,手里有一个尚未过期的 session。新的链上持有状态出现后,这个 session 怎么处理?
这是服务设计必须给出的答案。可以缩短会话的有效时间,也可以在敏感操作前重新核验,或者让状态变化触发已有授权失效。选择取决于服务承诺和可接受的延迟。转移的服务语义,需要包含这部分行为。
同样,如果资产协议支持 operator 或转移批准,也要单独解释它的效力。允许某个地址处理 NFT 的转移,不能自动被解释成允许它读取资源中的文件。资源服务接受怎样的委托,需要自己明确定义并核验。
图示为允许转移的权利;已有会话按对应服务规则处理。
真正的决定发生在执行处
把前面的判断放进一个假设的上传操作,问题就具体了。服务知道请求来自谁,接受相关发行人,确认当前权利允许向目标空间写入,还需要检查空间是否有可用额度。
额度是使用中的状态。如果两个上传同时到达,它们可能都读到相同的剩余空间。系统需要把额度检查与占用安排好,避免同一份余量被重复使用。类似地,一次性资格需要处理重复请求,执行失败后的重试也不能意外消耗两次。这些都是资源服务在执行权利时要处理的事。
这一层没有必要让用户每次都看见,但必须真实存在。登录时显示“验证成功”,而文件接口只认一个前端传来的资源 ID,授权判断就没有落到操作上。控制资源的接口需要执行相应规则,或核验一个范围明确、仍然有效的授权结果。
如果状态服务暂时不可用,也需要预先决定行为。暂停新操作、接受某个时间范围内的已验证状态,都有具体代价。服务对外承诺的即时性,应与这种选择一致。
用户的一次滑动,应该对应一个说得清的决定
理解这套设计,最直接的办法是把 DID Wallet 的出示流程和 DID Connect 的请求示例放在一起看。前者解释用户选择了什么,后者展示证据如何进入应用。资源服务则把这些证据与具体操作的授权规则、当前状态连接起来。
我觉得这也是判断 NFT 资源方案是否成立的一个实用标准:拿出一个真实操作,能不能说清它为何被允许,又会在什么条件下被拒绝。
用户不需要在上传文件之前学习凭证模型。应用却需要清楚地知道,谁针对哪个资源获得了什么权利,这项权利现在是否还有效。把这个决定落实到实际服务上,钱包里的 NFT 才真正连接到了资源。
参考