如何讓 authority 綁定 task goal、計畫或 workflow instance,避免被挪用到另一個任務?
同一個 Research Agent 同時處理 T-A「整理 Alice 的合約」與 T-B「產生公開市場摘要」。若它持有通用 documents:read scope,模型或 queue bug 只要把 task_id 換掉,就可能把 A 的 confidential documents 帶進 B 的 context。Task ID 放在 header 並不會自動形成安全邊界。
傳統 role 常描述穩定職責;Agent 在同一個 runtime 內切換多個自然語言目標,且可自行分解、重試、平行化。真正需要授權的不是「這個 Agent 能不能讀文件」,而是「這個 Agent 代表誰,為哪個 workflow instance,以哪個 purpose,對哪一組 resource 做哪個 Action」。
Agent A 的 grant 是 task=T-A, resources={contract/123}, actions={read}。攻擊者或模型將 request 改成 task=T-B, resource=customer/999,PEP 只驗 signature 與 role,於是跨 Task 讀取。另一個常見錯誤是 retry 使用舊 grant,但 Task 已完成;重播仍有權限。
Task grant 是可驗證的 authority object,不是 Agent 自報的 context。它應綁定 task_id、goal_digest、actor、subject、audience、allowed operations、canonical resource set、purpose、parent_task、sequence/nonce、expiry 與 completion state。自然語言 goal 先由 Orchestrator 正規化成 action constraints;模型不能直接決定 grant 內容。
RFC 9396 的 Rich Authorization Requests 用 authorization_details 表達比 scope 更豐富的授權細節,概念上適合承載「要對哪些 type/resource 做什麼」;本系列的 Task grant 仍需額外驗證 workflow 狀態與 action digest。若 Task 分支或 retry,應建立 child grant/新的 nonce,而不是複製原 grant;完成、取消或超時立即使 grant 不可用。
Agent 只帶 role=researcher 和可任意填寫的 task_id;PDP 以 role allow,Tool 自己相信 resource 名稱。完成 Task 後 token 仍可重播。
Task Service 由 User intent 建立 workflow instance,簽發只可縮小的 grant。PEP 驗證 grant 與 request 的 task/goal/resource/action digest 完全相符,並查 Task state;PDP 對每個 step 回傳 constraint。跨 Task 的 call 必須拿另一份 grant。
Agent 是 grant holder/consumer,不是 issuer。Task Service、Resource Catalog、Delegation Service 與 PDP 提供可信 facts;PEP 不接受 Agent 自報 purpose、owner 或 completion state。
User intent → workflow instance T-A → task grant G-A → Agent instance → PEP → Tool。同一 Agent 可同時持有 G-A/G-B,但每個 call 必須明確選一個 grant,且 task_id、audience、resource set 不可交叉。
allow 需同時滿足 grant.task == request.task、goal/action digest match、resource ∈ grant、Task active、nonce 未使用、未撤銷。否則 deny;重試使用新 nonce 並重新決策。

def allow(grant, request, state, used_nonce):
return (state == "active" and grant["task"] == request["task"]
and grant["goal"] == request["goal"]
and request["resource"] in grant["resources"]
and request["action"] in grant["actions"]
and request["nonce"] not in used_nonce)
g = {"task":"T-A", "goal":"contract-summary", "resources":{"contract/123"}, "actions":{"read"}}
ok = {"task":"T-A", "goal":"contract-summary", "resource":"contract/123", "action":"read", "nonce":"n1"}
cross = {**ok, "task":"T-B", "nonce":"n2"}
assert allow(g, ok, "active", set())
assert not allow(g, cross, "active", set())
assert not allow(g, ok, "completed", set())
assert not allow(g, ok, "active", {"n1"})
print("task-bound=allow cross-task/completed/replay=deny")
Day 23 會繼續追問:即使 Task grant 允許 read,讀出的資料是否仍能流向任意外部 Tool?