应用重启后,待处理的验证状态还在吗?

先在免费沙盒中跑通流程,再准备生产接入。

统计选择在本站复用,可随时关闭;账号关联另行同意。

应用重启后,待处理的验证状态还在吗?

你的入驻服务创建了一个托管身份验证会话,把它以待处理状态连同截止时间写入存储,然后进程在任何回调到达之前就重启了。你要做的决定是:本地状态是否足够持久,使重启后的进程能接着处理同一个会话,还是必须新建一个?本文逐步演示合成夹具:第二个进程打开同一个 SQLite 文件时,看到的是同一行待处理记录、同一个截止时间和同一个授权计数。本文也说明重启绝不能做的事:不能创建重复会话,不能凭空产生批准,也不能去轮询提供方。这里的一切都是本地合成示例,不验证真实签名,也不授予真实权限。

curl https://gridzen.ai/console/api/quickstart/trial \
  -H 'Content-Type: application/json' \
  -d '{"task": "identity_onboarding", "scenario": "success"}'

接入指南

问题:创建与回调之间发生重启

在托管身份入驻中,创建会话与得知结果之间存在一段空档。一次部署、一次崩溃或一次容器重新调度都可能恰好落在这段空档里。如果待处理状态只保存在内存中,重启后的进程就完全没有这个会话的记录。随之而来的是两种错误的捷径:一是新建会话,导致用户可能验证两次、两个回调相互竞争;二是把缺失的记录当作已经结束,直接放用户通过。读者真正要做的决定比看起来更窄:确认持久化存储而不是进程内存掌管待处理状态,并确认重启后的进程在采取任何行动之前先读取这一状态。夹具只针对一种情况回答这个问题:同一个 SQLite 数据库文件,被一个新的 Identity 实例重新打开。

夹具实际实现了什么

可下载示例把会话存放在一张 SQLite 表中,字段包括 id、tenant、subject、status、deadline、granted、grants、policy_version 和 allow_action,另有一张以会话和事件为主键、用于去重的 events 表。用某个路径构造 Identity 时会执行 CREATE TABLE IF NOT EXISTS,因此重新打开已有文件会保留原有行,而不会把它们清空。create 插入一行待处理记录,deadline 等于当前时间加上 timeout;对同一个会话 id 再次调用 create 会触发 SQLite 完整性错误,而不是静默覆盖,命令行入口会把这种情况报告为 existing_fixture。read 返回存储中的 status、deadline、permission_granted、grant_count、policy_version 和 allow_action,并且 live_permission_changed 始终为 false。夹具只接受合成回调:事件 id 必须以 fixture- 开头,mode 必须是 synthetic_fixture,source 必须是 fixture_callback;其他任何输入都会抛出 PRODUCTION_SIGNATURE_VERIFIER_NOT_IMPLEMENTED。重启测试通过指南中列出的重启测试和超时恢复单元测试来运行。这与匿名的三场景 HTTP 试用不是一回事,那个试用也不是可下载的四分支电话示例。

一个假设性的完整场景

假设进程 A 打开一个数据库文件,在时间 0 调用 create,使用默认的 60 秒超时。创建输出为:mode 为 synthetic_fixture,state 为 simulated,session 为 fixture-session,hosted_link 指向 example.invalid 域名,link_executable 为 false,next 为 send_real_provider_link_only_after_authorization。随后进程 A 停止。进程 B 在同一个文件上新建 Identity 并调用 read:status 为 pending,deadline 为 60,permission_granted 为 false,grant_count 为 0。没有任何数据丢失,也不存在第二个会话。分支一:在时间 1 到达一个合成的 Approved 事件,且 policy 为 true。预期输出为 status approved、deadline 60、permission_granted true、grant_count 1、live_permission_changed false、policy_version 1、allow_action true。再次投递同一个事件 id,返回完全相同的值,grant_count 仍为 1。分支二:没有回调到达,进程 B 在时间 61 调用 recover。预期为 status expired、deadline 60、permission_granted false、grant_count 0、next 为 review_or_authorized_provider_refresh、provider_polled 为 false。之后针对已过期会话的事件不会改变其过期状态。

仍由调用方负责的失败处理

夹具负责把错误暴露出来,如何处理则由你的应用决定。对数据库中不存在的会话 id 调用 read 会抛出 SESSION_NOT_FOUND;在重启之后,这通常意味着进程打开了另一个文件,例如一个新的临时路径或一个新的容器卷。应把它当作需要告警的配置故障,而不是悄悄新建会话的理由。如果事件的 tenant 或 subject 与存储的行不匹配,或者指向一个未知会话,则会抛出 BINDING_MISMATCH,且不改变任何状态。recover 只在本地使会话过期,从不查询提供方;在真实集成中,下一步应是人工审核或经授权的状态刷新,这些需要你自己构建,本示例并未建立这些能力。缺失的回调永远不会变成批准。

建议的应用设计,而非已实现的行为

对于生产环境,我们建议:把数据库放在比进程寿命更长的存储上;在启动时执行一次扫描,读取 pending、review 和 unknown 状态的会话,对已超过截止时间的会话调用与 recover 等效的逻辑;如果预期的数据库不存在,就拒绝启动。你还需要针对具体提供方的签名验证,以及有边界的状态对账。以上这些都不在夹具中。同样,夹具中的 permission_granted 只是本地记录;合成夹具中的权限不是真实的业务权限,Approved 状态也不是对真实用户采取行动的无条件许可。

验收清单

在判断沙盒集成是否适合之前:运行重启测试和超时恢复测试,确认两者都通过;确认在同一文件上的第二个实例读到 pending、deadline 为 60、grant_count 为 0;确认重复调用 create 不会覆盖原有行;确认重复事件使 grant_count 保持为 1;确认截止时间之后调用 recover 返回 expired 且 provider_polled 为 false;确认未知会话在 read 时得到 SESSION_NOT_FOUND、在 event 时得到 BINDING_MISMATCH;并以书面形式记录签名验证、提供方刷新和真实权限仍未构建。测试通过只说明本地状态处理正确,不代表生产环境成功。

运行已检查的模拟示例

下载并解压 https://gridzen.ai/downloads/verification-delivery.zip,进入 verification_delivery,用 Python 3.10+ 运行。此本地示例不联网;独立身份实验请使用新的数据库。

python3 delivery.py identity --db /tmp/gridzen-article-fixture.sqlite

来源与范围

https://gridzen.ai/guides/missing-verification-webhook/ https://gridzen.ai/guides/current-capabilities/ 依据已检查的来源快照与本地模拟;建议的业务规则属于设计建议,不代表已证明的生产行为。内容使用 AI 辅助写作,并经过独立审稿。

另行体验三种沙盒结果 创建账号,继续项目接入

下载不等于运行成功;本地示例与在线沙盒分别测试。

接入你的应用

运行三种模拟结果 · 需要接入帮助

连接免费沙盒账号

号码基础有效性不证明持有权或送达,不提供短信验证码。业务放行由你的系统判断。