Skip to content

fix: harden setup token exchange#254

Open
texuf wants to merge 1 commit into
mainfrom
codex/audit-setup-tokens
Open

fix: harden setup token exchange#254
texuf wants to merge 1 commit into
mainfrom
codex/audit-setup-tokens

Conversation

@texuf

@texuf texuf commented May 1, 2026

Copy link
Copy Markdown
Contributor

Summary:

  • stores setup tokens as SHA-256 hashes instead of raw bearer values
  • consumes setup tokens with a conditional transaction before issuing device API keys
  • adds coverage for hashed storage and concurrent exchange replay

Validation:

  • bun run typecheck in apps/server passed
  • bun run test test/upload.test.ts could not run locally: Postgres refused connection on localhost:5442

Note

Medium Risk
Touches credential issuance paths (setup-token generation, redemption, and API key format), so mistakes could block device sign-in or weaken token security. Changes are contained to token handling and covered by new tests for hashing and concurrent redemption.

Overview
Hardens the setup-token flow by storing setup tokens as SHA-256 hashes (returning only the raw token to the caller) and by redeeming tokens transactionally during /v1/auth/exchange to prevent race/replay (conditional UPDATE ... WHERE redeemed=false AND expiresAt>now).

API key generation is switched from UUIDs to opaque base64url bearer tokens via generateOpaqueToken, and the exchange path now consistently hashes the presented setup token before lookup; tests add coverage for hashed-at-rest storage and for allowing only one concurrent exchange to succeed.

Reviewed by Cursor Bugbot for commit dec59fd. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Security

    • Setup tokens are now hashed and stored securely.
    • API key generation updated with enhanced token format.
  • Bug Fixes

    • Fixed race condition handling for single-use setup token redemption.
    • Improved transactional consistency during device and API key operations.

@coderabbitai

coderabbitai Bot commented May 1, 2026

Copy link
Copy Markdown

Walkthrough

This pull request refactors setup token handling across the authentication and user management layers. The changes introduce opaque token generation for setup tokens, implement token hashing at storage time, and make token redemption atomic via database transactions. The /v1/auth/exchange endpoint now hashes incoming setup tokens, looks them up via hash, and performs device upsert and API key operations within a single transaction with consistent timestamps. Tests validate that setup tokens are stored only as hashes and that concurrent redemption attempts result in exactly one successful exchange.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: harden setup token exchange' clearly summarizes the main change: improving security of the setup token exchange mechanism through hashing and transaction-based atomicity.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/audit-setup-tokens

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/server/test/upload.test.ts (1)

96-99: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

**Test is broken: generateApiKey no longer returns UUID format.**The pipeline failure shows that generateApiKey() now returns a base64url-encoded string (e.g., "t7y0E1goaejjudnpdAEjv4O7_QvbTfZG6VyhQqjNnVs") instead of a UUID. 32 bytes produces ~43 characters in base64url encoding.

The test assertion at line 98 expects UUID format but the implementation has changed. This test must be updated to match the new token format.

🐛 Proposed fix: Update test to validate base64url format
 describe("token utilities", () => {
-  it("generateApiKey returns UUID format", () => {
+  it("generateApiKey returns base64url format", () => {
     const key = generateApiKey();
-    expect(key).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
+    // 32 bytes base64url-encoded = 43 characters (A-Za-z0-9_-)
+    expect(key).toMatch(/^[A-Za-z0-9_-]{43}$/);
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/test/upload.test.ts` around lines 96 - 99, The test still asserts
UUID format but generateApiKey() now returns a base64url-encoded token; update
the test for generateApiKey to assert the new format by checking the value from
generateApiKey() matches a base64url character set (e.g., /^[A-Za-z0-9\-_]+$/)
and the expected length for a 32-byte token encoded in base64url (~43
characters), replacing the UUID regex assertion in the it("generateApiKey
returns UUID format", ...) block.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@apps/server/test/upload.test.ts`:
- Around line 96-99: The test still asserts UUID format but generateApiKey() now
returns a base64url-encoded token; update the test for generateApiKey to assert
the new format by checking the value from generateApiKey() matches a base64url
character set (e.g., /^[A-Za-z0-9\-_]+$/) and the expected length for a 32-byte
token encoded in base64url (~43 characters), replacing the UUID regex assertion
in the it("generateApiKey returns UUID format", ...) block.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c904c601-f545-4589-a69a-f10cb3bfdb22

📥 Commits

Reviewing files that changed from the base of the PR and between 11cbdda and b519baa.

📒 Files selected for processing (4)
  • apps/server/src/auth/github.ts
  • apps/server/src/auth/tokens.ts
  • apps/server/src/user/routes.ts
  • apps/server/test/upload.test.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b519baa. Configure here.

/** Generate a random API key. */
export function generateApiKey(): string {
return crypto.randomUUID();
return generateOpaqueToken();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Changed generateApiKey output breaks existing UUID format test

Medium Severity

generateApiKey now returns a base64url-encoded random string via generateOpaqueToken(), but an existing test at line 96–99 of upload.test.ts asserts it matches a UUID v4 regex. That test will always fail because base64url output doesn't conform to the xxxxxxxx-xxxx-4xxx-… pattern. The test wasn't updated alongside the implementation change.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b519baa. Configure here.

@texuf

texuf commented May 5, 2026

Copy link
Copy Markdown
Contributor Author

looks like this is handled in #289

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.

1 participant