Skip to content

feat(approvals): actionable approval links — single-use tokens + session-less confirm page (ADR-0043) - #1765

Merged
os-zhuang merged 1 commit into
mainfrom
feat-approvals-actionable-links
Jun 12, 2026
Merged

feat(approvals): actionable approval links — single-use tokens + session-less confirm page (ADR-0043)#1765
os-zhuang merged 1 commit into
mainfrom
feat-approvals-actionable-links

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Summary

Closes#1743. Approvers act straight from an email/IM notification without signing in — implemented exactly along ADR-0043's threat table (docs/adr/0043-actionable-approval-links.md), every row enforced and tested:

axisenforcement
scopeone token = one request + one action + one approver
storageSHA-256 hashes only in the new sys_approval_token; raw 256-bit tokens returned once
single-useconsumed before deciding — failed decide still burns; replay → "已使用" page
TTL72h default
identitydecision audited as the bound approver (Via action link), never the page visitor
invalidationredemption re-checks the live request — decided / recalled / reassigned-away slots answer with explanatory pages
scanner-proofGET /api/v1/approvals/act renders the bilingual confirm page only; the decision happens exclusively on the POST form

remind() integrates with per-approver fan-out: each concrete identity gets its own approve/reject links in the notification payload (role:* literals keep the plain nudge). publicBaseUrl plugin option for absolute links in outbound email.

Test plan

  • plugin-approvals: 69 tests (5 new: hash-only storage + binding, redeem-as-approver + replay burn, peek-never-mutates, the four dead-token paths incl. reassigned slots, per-approver reminder links — all 4 URLs distinct) ✅
  • Live e2e on showcase: remind → notification payload carried personal approve/reject URLs → opened the approve link in the browser (session-less confirm page with the full business summary, acting-as ada@example.com) → clicked Approve → "✅ Approved · 已通过" result page → audit row approve by ada@example.com, "Via action link" (NOT the signed-in Dev Admin). Replay → "Already used"; the sibling reject token → "Already decided"; garbage → "Invalid link" ✅

🤖 Generated with Claude Code

…ion-less confirm page (ADR-0043)
Approvers act straight from a notification without signing in. The
token behind the link is deliberately weakened on every axis of the
ADR-0043 threat table:
- scope: one token = one request + one action + one approver
- storage: SHA-256 hash only (sys_approval_token; a DB leak yields no
usable links); raw 256-bit tokens are returned exactly once
- single-use: consumed BEFORE deciding — a failed decide still burns
the token, replays render "already used"
- TTL: 72h default
- identity: the decision is audited as the bound approver ("Via action
link"), never a system actor or the page visitor
- invalidation: redemption re-checks the live request — decided,
recalled, or reassigned-away slots all answer with explanatory pages
- scanner-proof: GET /api/v1/approvals/act renders the bilingual
confirm page only; the decision happens exclusively on the POST form
(mail-gateway link prefetchers cannot approve)
remind() integrates: per-approver fan-out where each concrete identity
(user id / email) receives its own approve/reject links in the
notification payload; role:* literals keep the plain nudge. Deployments
set publicBaseUrl for absolute links in outbound email.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@os-zhuang
os-zhuang merged commit 3b2e729 into mainJun 12, 2026
1 of 2 checks passed
@os-zhuang
os-zhuang deleted the feat-approvals-actionable-links branch June 12, 2026 05:40
@vercel

vercelBot commented Jun 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specBuildingBuildingPreview, CommentJun 12, 2026 5:40am

Request Review

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(approvals): actionable email/IM notifications — single-use approval tokens

1 participant

@os-zhuang