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:

axis enforcement
scope one token = one request + one action + one approver
storage SHA-256 hashes only in the new sys_approval_token; raw 256-bit tokens returned once
single-use consumed before deciding — failed decide still burns; replay → "已使用" page
TTL 72h default
identity decision audited as the bound approver (Via action link), never the page visitor
invalidation redemption re-checks the live request — decided / recalled / reassigned-away slots 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

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 main Jun 12, 2026
1 of 2 checks passed
@os-zhuang
os-zhuang deleted the feat-approvals-actionable-links branch June 12, 2026 05:40
@vercel

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Building Building Preview, Comment Jun 12, 2026 5:40am

Request Review

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

Labels

documentation Improvements or additions to documentation size/l tests tooling

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