Skip to content

fix(auth): reject RotateAPIKey when SoftDelete affects 0 rows - #822

Merged
steventohme merged 6 commits into
mainfrom
devin/rotate-api-key-soft-delete-race
Jul 31, 2026
Merged

fix(auth): reject RotateAPIKey when SoftDelete affects 0 rows#822
steventohme merged 6 commits into
mainfrom
devin/rotate-api-key-soft-delete-race

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Summary

Compliance rewrite of #819 by @rohith500 (thanks!). Fixes #817: RotateAPIKey no longer mints a successor when its own SoftDelete matches 0 rows (a lost race against a concurrent Rotate or a Delete), which previously produced untracked "zombie" credentials.

Production logic and tests are preserved verbatim from #819; the only delta is trimming godoc/SQL comments to the repo's comment convention (root CLAUDE.md line 15: concise, no multi-paragraph, no pre-fix narrative, no caller references).

Behavior:

SoftDeleteModelRouterAPIKey: :exec -> :execrows
APIKeyRepository.SoftDelete: error -> (int64, error)
RotateAPIKey: n == 0 -> ErrAPIKeyNotFound (no IssueAPIKey)
DeleteAPIKey: still discards count (idempotent success unchanged)

Coverage (unchanged from #819): in-memory fake-repo race tests in internal/auth/rotate_race_test.go (concurrent Rotate → exactly one success; Rotate-vs-Delete → no zombie), plus live-Postgres repro scripts under scripts/{rotate_key_race_check,rotate_key_race_ext_check,upsert_external_key_race_check} (per the AGENTS.md rule that DB-backed checks live in scripts/, not internal/*_test.go).

make generate + make precommit green.

Co-authored-by: rohith500 rohithreddy2202@gmail.com

Link to Devin session: https://app.devin.ai/sessions/03fe47e3946a4829b431a2e2a35c1606
Requested by: @steventohme

rohith500 and others added 3 commits July 22, 2026 21:06
Deterministic fake-repo tests for concurrent RotateAPIKey and
Rotate-vs-Delete SoftDelete races. Both fail on current :exec SoftDelete
behavior (loser still mints a successor).

Signed-off-by: N Rohith Reddy <rohithreddy2202@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Switch SoftDeleteModelRouterAPIKey to :execrows and have RotateAPIKey
bail with ErrAPIKeyNotFound when SoftDelete matches no row, closing the
concurrent-rotate and rotate-vs-delete zombie-key races from #817.
DeleteAPIKey keeps discarding the count for idempotent success.

Also lands the DB-backed repro scripts referenced by #817.

Signed-off-by: N Rohith Reddy <rohithreddy2202@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: N Rohith Reddy <rohithreddy2202@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: rohith500 <rohith500@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@greptile-apps

greptile-apps Bot commented Jul 23, 2026

Copy link
Copy Markdown

PR author is not in the allowed authors list.

@claude

claude Bot commented Jul 23, 2026

Copy link
Copy Markdown

Claude finished @devin-ai-integration[bot]'s task —— View job


🤖 Claude Code Review

  • Gather diff of Go changes
  • Analyze added comment lines for verbosity
  • Post review if issues found

Posted advisory comment-length nits on 2 blocks:

  1. internal/auth/api_key.go lines 35–37 — 3-line interface godoc restates the return type; fits in one sentence.
  2. scripts/rotate_key_race_check/main.go lines 83–86 — 4-line inline comment explaining timing; fits in two lines.

@workweave-bot workweave-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Advisory only — comment-length nits. Won't block merge.

Comment thread internal/auth/api_key.go Outdated
Comment thread scripts/rotate_key_race_check/main.go Outdated
@claude

claude Bot commented Jul 23, 2026

Copy link
Copy Markdown

Claude finished @devin-ai-integration[bot]'s task —— View job


Claude finished @devin-ai-integration[bot]'s taskView job


🤖 Claude Code Review

  • Gather diff of Go changes
  • Analyze added comment lines for verbosity
  • Post review if issues found

Posted advisory comment-length nits on 2 blocks in internal/auth/rotate_race_test.go:

  1. Lines 189–191 — 3-line comment before TestRotateAPIKey_ConcurrentRace_OnlyOneSucceeds; the function name already conveys the scenario, so the comment restates rather than adds WHY.
  2. Lines 240–242 — 3-line comment before TestRotateAPIKey_LosingToDelete_DoesNotIssue; same issue — trim to the non-obvious constraint (no zombie on lost SoftDelete).

@workweave-bot workweave-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Advisory only — comment-length nits. Won't block merge.

Comment thread internal/auth/rotate_race_test.go Outdated
Comment thread internal/auth/rotate_race_test.go Outdated
@claude

claude Bot commented Jul 23, 2026

Copy link
Copy Markdown

Claude finished @devin-ai-integration[bot]'s task —— View job


🤖 Claude Code Review

  • Gather diff of Go changes
  • Analyze added comment lines for verbosity
  • Post review if issues found

Posted advisory comment-length nits on 2 blocks:

  1. internal/auth/rotate_race_test.go lines 17–20 — 4-line struct godoc restates what the SoftDelete method makes obvious; fits in two lines.
  2. scripts/rotate_key_race_check/main.go lines 10–16 — 7-line timing note; the pool-limiter and :exec path sentences are filler — the TOCTOU insight fits in three lines.

View job

@workweave-bot workweave-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Advisory only — comment-length nits. Won't block merge.

Comment thread internal/auth/rotate_race_test.go Outdated
Comment thread scripts/rotate_key_race_check/main.go Outdated
@claude

claude Bot commented Jul 23, 2026

Copy link
Copy Markdown

Claude finished @devin-ai-integration[bot]'s task —— View job


🤖 Claude Code Review

  • Gather diff of Go changes
  • Analyze added comment lines for verbosity
  • Post review if issues found

Posted advisory comment-length nits on 3 blocks:

  1. scripts/rotate_key_race_check/main.go lines 1–17 — Package doc has a filler paragraph (lines 6–8) explaining "It is a separate main package (not a _test.go)…" — the scripts/ path and the if dsn == "" guard already communicate this; drop to 13 lines.
  2. scripts/rotate_key_race_check/main.go lines 73–75 — 3-line func doc narrates implementation steps; fits in two.
  3. scripts/rotate_key_race_ext_check/main.go lines 176–178 — 3-line func doc; "Expected buggy outcome" sentence is filler — two lines capture the constraint.

@workweave-bot workweave-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Advisory only — comment-length nits. Won't block merge.

Comment thread scripts/rotate_key_race_check/main.go
Comment thread scripts/rotate_key_race_check/main.go
Comment thread scripts/rotate_key_race_ext_check/main.go
@steventohme
steventohme merged commit a064e63 into main Jul 31, 2026
11 checks passed
@steventohme
steventohme deleted the devin/rotate-api-key-soft-delete-race branch July 31, 2026 00:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RotateAPIKey race: concurrent rotate/delete calls on the same key mint zombie successor keys

3 participants