Skip to content

fix(auth): claim device codes atomically - #654

Merged
IvGolovach merged 1 commit into
junhoyeo:mainfrom
IvGolovach:codex/device-auth-atomic-20260601
Jun 4, 2026
Merged

fix(auth): claim device codes atomically#654
IvGolovach merged 1 commit into
junhoyeo:mainfrom
IvGolovach:codex/device-auth-atomic-20260601

Conversation

@IvGolovach

@IvGolovach IvGolovach commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Make device-code authorization claim the code in one guarded database update instead of selecting first and updating later.
  • Reject already-claimed, expired, or missing user codes through the same atomic predicate.
  • Add regression coverage proving the authorize route uses guarded update predicates and does not reintroduce the stale select-before-update path.

Why

The previous authorize flow checked device-code eligibility before the update, then updated by row id. Concurrent requests could both observe an eligible code and race to claim it, which risks binding one device code to the wrong user or overwriting an existing claim. The route now lets the database perform the eligibility check and claim together, so only one request can win.

Diff scope

  • packages/frontend/src/app/api/auth/device/authorize/route.ts: replaces the select-before-update flow with a single guarded UPDATE ... WHERE user_code AND expires_at > now AND user_id IS NULL RETURNING id.
  • packages/frontend/__tests__/api/deviceAuthorize.test.ts: adds route coverage for the atomic claim behavior and the stale/invalid-code rejection path.

Branch integrity

  • Base branch: main.
  • Validated base SHA: a86e688d620939d2c973c6d5625baa815ea223d7.
  • Ahead/behind: 0 behind / 1 ahead against origin/main.
  • Merge base: a86e688d620939d2c973c6d5625baa815ea223d7.
  • Fast-forward safety: origin/main is an ancestor of this branch.

Commit integrity

  • Introduced commit: 8df5fe3af173d91d2e946318608c6fc6e191996c fix(auth): claim device codes atomically.
  • The PR contains one logical change scoped to device-code authorization correctness and regression coverage.
  • Ledger: not applicable - not required for this change family.
  • Version: not applicable - no CLI package or release manifest changed.

Diff hygiene

  • git diff --name-status origin/main...HEAD: only the device authorize route and its focused API test changed.
  • git diff --check origin/main...HEAD: PASS, no output.

Validation mode and proof

Mode 3 - auth-route runtime change, because this changes device-code authorization semantics and relies on database predicate correctness for race safety.

  • TDD red proof: bun run test __tests__/api/deviceAuthorize.test.ts failed before the implementation because the update predicate only targeted deviceCodes.id instead of guarding userCode, expiresAt, and userId.
  • bun run test __tests__/api/deviceAuthorize.test.ts __tests__/api/devicePoll.test.ts: PASS, 2 files and 7 tests.
  • bun run lint src/app/api/auth/device/authorize/route.ts __tests__/api/deviceAuthorize.test.ts: PASS, no output.
  • git diff --check origin/main...HEAD: PASS, no output.
  • Not run: full frontend test suite - not required for selected validation mode because targeted authorize and poll route tests cover the changed auth path; required remote CI will run after the PR is opened.

Required remote gates

Pending - GitHub Actions, Vercel, and mergeability checks will run after the PR is opened.

Migration notes

Not applicable - no database migration changed.

Runtime safety

The route keeps the same request/response contract and error status while moving the eligibility check into the database write. It does not change polling behavior, token issuance, session lookup, expiry duration, or device-code generation. No invariant regression introduced.

Documentation integrity

Not applicable - no docs, commands, or runbooks changed.

Rollback plan

Rollback: revert this PR. DB downgrade: not applicable. Data repair: not applicable. Operational caveats: reverting would restore the select-before-update race in device-code authorization.

Known residual risks

Remote CI and GitHub mergeability are pending until the PR is opened. This relies on the existing database update semantics and does not add schema-level uniqueness or locking because the guarded single-row update is sufficient for the current device-code table contract.


Summary by cubic

Make device-code authorization atomic to remove race conditions. The POST /api/auth/device/authorize route now claims codes in a single guarded update and returns clear errors for invalid, expired, or already-claimed codes.

  • Bug Fixes
    • Replaced select-before-update with one guarded UPDATE ... WHERE user_code AND expires_at > now AND user_id IS NULL RETURNING id.
    • Added focused tests for atomic claim success and invalid/expired/already-claimed paths to prevent regressions.

Written for commit 8df5fe3. Summary will update on new commits.

Review in cubic

Validation
* Validation tier: Tier 3 - auth-route runtime change, because the update touches device-code authorization semantics.
* TDD red: bun run test __tests__/api/deviceAuthorize.test.ts: FAIL before implementation, update predicate was deviceCodes.id only instead of userCode/expiresAt/userId guards.
* bun run test __tests__/api/deviceAuthorize.test.ts: PASS, 2 tests.
* bun run test __tests__/api/deviceAuthorize.test.ts __tests__/api/devicePoll.test.ts: PASS, 7 tests.
* bun run lint src/app/api/auth/device/authorize/route.ts __tests__/api/deviceAuthorize.test.ts: PASS.
* git diff --check: PASS.
* git diff --cached --check: PASS.
* Ledger: not applicable - not required for selected validation tier/change family.
* Version: not applicable - no CLI package or release manifest changed.
* Not run: full frontend test suite - not required for selected validation tier; targeted authorize and poll route tests cover the changed auth path.

Rollback
* git revert HEAD
@vercel

vercel Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
tokscale Ready Ready Preview, Comment Jun 1, 2026 4:04am

Request Review

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No issues found across 2 files

Re-trigger cubic

@IvGolovach
IvGolovach merged commit 9c09473 into junhoyeo:main Jun 4, 2026
7 checks passed
@IvGolovach
IvGolovach deleted the codex/device-auth-atomic-20260601 branch June 4, 2026 22:27
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.

2 participants