Skip to content

fix(api): answer 401 before looking up the execution on the email-link routes - #594

Merged
cristim merged 2 commits into
mainfrom
fix/token-actions-no-existence-oracle
Oct 6, 2026
Merged

cristim merged 2 commits into
mainfrom
fix/token-actions-no-existence-oracle

Conversation

@cristim

@cristim cristim commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Closes #435

Approve, cancel and revoke by email link need a session on every branch (the token alone never authorizes). A caller without a session now gets the same 401 before the execution is loaded, so a missing and an existing id are indistinguishable. Before: missing 404; existing 401 (token) or 403 CSRF (no token).

  • New requireSessionBeforeLookup in approvePurchase, cancelPurchase and revokeViaEmailToken (POST; GET only renders the form).
  • cancelPurchase uses getExecutionOr404 (also guards a nil row).
  • openapi.yaml: 401/403 wording on approve, cancel, revoke. No new status codes.
  • Signed-in callers still get 404 for a missing id; valid-token flows unchanged. Does not touch fix(api): expired-token status codes differ across approve, cancel, revoke and RI-exchange token actions #587.
  • Test through HandleRequest: approve/cancel/revoke x no token, valid token, wrong token, missing vs existing, same status and body. Fails on main (404 vs 401/403).
  • Three older unit tests encoded the unauthenticated 403/404/500 behavior and were updated.

🤖 Generated with Claude Code

Accepted residual (outside #435, which covers the no-session case only): authenticated callers can still see an existence difference, e.g. a session without RBAC or contact-email match gets 404 for a missing id vs 403/409 for an existing one, and a signed-in caller without the CSRF header gets 403 vs 404. A follow-up will be filed.

…k routes

Approve, cancel and revoke by email link need a session on every branch, so a caller without one now gets the same 401 whether or not the execution exists. Before, a missing id gave 404 while an existing one gave 401 (token) or 403 (no token), which let an unauthenticated caller probe for execution ids.

Closes #435
@cristim cristim added priority/p3 Polish / idea / may never ship effort/xs Trivial / one-liner type/security Security finding labels Oct 6, 2026
@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

This review includes 7 billable files and costs up to $1.75.

  • Ask an admin to make reviews automatic

Open in CodeRabbit

Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing.

Or wait 20 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 64 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: LeanerCloud/cloud-commitments-platform/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: cb133a87-e6da-4c99-972d-b5a1c990849a
📥 Commits

Reviewing files that changed from the base of the PR and between 165039d and 62dc19a.

📒 Files selected for processing (7)
  • internal/api/coverage_extras_test.go
  • internal/api/handler_purchases.go
  • internal/api/handler_purchases_test.go
  • internal/api/handler_purchases_token_oracle_test.go
  • internal/api/openapi.yaml
  • internal/server/app_rate_limiter_integration_test.go
  • internal/server/lambda_coverage_test.go
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

…ith the 401-first email-link routes

The base64 form-body test now signs in so the decoded token still decides the response, the rate-limiter integration test expects 401 for the unauthenticated approve/cancel flood, and the oracle test also covers an invalid bearer and a bogus x-authorization header.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/xs Trivial / one-liner priority/p3 Polish / idea / may never ship type/security Security finding

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sec(api): unauthenticated cancel/revoke distinguish missing (404) from existing (401) executions

1 participant