身份验证 API 回调:先持久化状态,再等待
你的开户服务创建托管验证会话后,便开始等待回调。等待期间可能出现两种问题:进程重启,或回调始终没有到达。如果待处理状态只保存在内存中,重启就会丢失它;如果没有任何机制强制截止时间,缺失的回调会让用户一直卡住,甚至有人会把沉默当作通过。本文演示合成示例 verification_delivery 中经过检验的本地重启分支和回调缺失分支,给出精确的 fixture 输出,并帮助你判断沙盒集成是否适合你的任务。该 fixture 只接受合成回调,在本地让会话过期,从不轮询任何提供方。
curl https://gridzen.ai/console/api/quickstart/trial \
-H 'Content-Type: application/json' \
-d '{"task": "identity_onboarding", "scenario": "success"}'问题与决策
身份验证 API 的回调是一种异步承诺:提供方会在稍后告诉你结果,而在此之前发生的一切都由你的应用负责。你要做的决定非常具体:待处理状态存放在哪里,适用什么截止时间,截止时间过去后仍没有回调时应该怎么办。指南给出的规则很简短:持久化待处理状态和截止时间;通过有界重试、经授权的状态刷新或人工审核进行恢复;绝不把缺失的回调变成批准。可下载示例演示的是这条规则中的持久化与本地过期部分,并不演示提供方签名校验或状态对账,这些是生产实现需要另外完成的工作。
fixture 实际实现了什么
示例把会话保存在 SQLite 中。调用 create 会写入一行记录,状态为 pending,deadline 等于 now 加 timeout;使用默认值时为 0 加 60,即 deadline 为 60。标识符必须以 fixture- 开头,返回的 hosted_link 指向一个无效的示例域名,且 link_executable 为 false。event 方法只接受 mode 为 synthetic_fixture、source 为 fixture_callback 的事件,其他取值会抛出 PRODUCTION_SIGNATURE_VERIFIER_NOT_IMPLEMENTED。recover 方法在会话的 deadline 小于或等于传入时间、且状态仍为 pending、review 或 unknown 时,把会话标记为 expired。它返回 provider_polled 为 false:恢复只是本地时钟比较,而不是向提供方查询。这里记录的任何权限都只是本地 fixture 标记;live_permission_changed 始终为 false,合成 fixture 中的权限绝不等于真实的业务权限。
假设场景:先重启,再无回音
假设一位卖家在时间 0 开始开户。你的服务调用 create,得到会话 fixture-session,并以 pending 状态和 deadline 60 保存。到时间 20,进程重启。由于状态保存在 SQLite 中,重启后的进程打开同一个数据库文件并调用 read。预期输出仍显示 status 为 pending、deadline 为 60、permission_granted 为 false、grant_count 为 0;不需要新建会话,而且再次创建 fixture-session 会因主键冲突而失败,这正提示你应当恢复原会话而不是重复创建。之后一直没有回调到达。到时间 61,定时任务调用 recover。fixture 的预期输出为:mode synthetic_fixture,session fixture-session,status expired,deadline 60,permission_granted false,grant_count 0,live_permission_changed false,policy_version 1,allow_action true,next review_or_authorized_provider_refresh,provider_polled false。注意 allow_action 为 true 只描述会话的策略设置,并不授予任何东西。如果过期后才到达一个迟来的合成 Approved 事件,会话仍保持 expired。
仍由你的应用负责的失败处理
fixture 只返回下一步动作,并不执行它。会话过期后,你所提议的设计必须在人工审核与针对真实提供方的经授权状态刷新之间做出选择,并限制尝试次数、记录结果。要把以下失败区分开来:如果事件对应会话的 tenant 与 subject 不匹配,会抛出 BINDING_MISMATCH,应把它视为路由或安全问题,而不是拒绝;读取未知会话会抛出 SESSION_NOT_FOUND,不要悄悄重建会话;重复事件会返回当前状态,不会再次授权。绝不要把 expired 解读为对此人的拒绝,也不要解读为批准。在生产环境中,签名校验、与提供方的对账以及客户自己的权限规则仍是独立的工作;本示例并不具备这些能力,而且一位新用户完整走通真实托管流程的情况仍未经测试。
如何运行已检验的分支
下载示例,解压后进入 verification_delivery 目录。它需要 Python 3.10 或更高版本,不需要凭据,也不需要网络。指南点名了两个具体测试:test_delivery.DeliveryTests 中的 test_restart 和 test_timeout_recovery;另外还有完整的 test_delivery 测试集,覆盖模拟状态转换、绑定校验、重复回调、重启和超时。指南还展示了一个针对本地回环地址的 recover 请求;那是独立的匿名 HTTP 试用,与可下载示例的分支不是一回事,二者都不会查询提供方。测试通过只说明本地代码能正确处理这些状态,并不证明生产环境成功或已被采用;真实路由是否可用需要另行确认。
验收清单
在判断沙盒集成是否适合你的任务之前,请逐项确认:在发送任何链接之前,待处理会话已连同截止时间写入持久存储;重启后的进程从同一个数据库恢复同一个会话,而不是创建重复会话;定时恢复任务会把 deadline 与当前时间比较,并把已超时的 pending、review 或 unknown 会话转为 expired;fixture 中过期输出带有 provider_polled 为 false,而你真实的刷新路径经过授权且有次数上限;缺失的回调绝不会产生 permission_granted 为 true;迟到、重复和绑定不匹配的事件都不会产生新的授权;签名校验与提供方对账已被列为尚未完成的生产工作。如果这些条目符合你的需要,请先运行合成测试,然后再考虑保存一个可选的沙盒请求。
运行已检查的模拟示例
下载并解压 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 辅助写作,并经过独立审稿。
下载不等于运行成功;本地示例与在线沙盒分别测试。