feat: Better Auth Phase 4 — CLI device login (uploads login)#112
Conversation
Adds the RFC 8628 device-authorization flow so `uploads login` works from the CLI without an enrollment code, plus the session-authenticated `POST /v1/tokens` endpoint that mints workspace tokens from a device-flow session. - apps/auth: deviceAuthorization + bearer plugins, fail-closed validateClient allowlisting the static `uploads-cli` client id; device_code table + migration. - apps/api: POST/GET /v1/tokens (session/bearer auth over the AUTH binding, single-grant validation, org-membership gate, minting-user column on auth_tokens); mounted before workspaceAuth. - apps/web: /device approval page + device helpers in auth-client. - CLI: `uploads login` device flow (code → browser approval → poll → mint → save); --code enrollment path retained; changeset. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…01-e3c3e0 # Conflicts: # apps/auth/src/auth.ts # apps/auth/src/schema.ts # apps/web/src/lib/auth-client.ts
…01-e3c3e0 # Conflicts: # apps/api/src/index.ts
- Hoist membershipsForUser into org-workspaces.ts (the D4 org↔workspace seam) and import it in both routes/me.ts and routes/tokens.ts, replacing two drifted copies with one throw-on-outage implementation. - routes/tokens.ts: use loadWorkspaceRecord for the KV lookup and run the workspace/org/membership lookups concurrently on the mint path. - CLI login: reuse the existing parseScopes helper instead of a second scope parser. - device.astro: factor the shared approve/deny handler. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are limited based on label configuration. 🏷️ Required labels (at least one) (2)
🚫 Excluded labels (none allowed) (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds RFC 8628 device authorization for ChangesDevice Login and Token Minting
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant CLI
participant Auth
participant Browser
participant API
CLI->>Auth: Request device code
Auth-->>CLI: Device code and verification URL
CLI->>Browser: Open verification URL
Browser->>Auth: Approve device
CLI->>Auth: Poll for bearer token
Auth-->>CLI: Bearer token
CLI->>API: Mint workspace token
API-->>CLI: Workspace token
Possibly related PRs
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
CodeRabbit (@coderabbitai) review |
✅ Action performedReview finished.
|
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
uploads-web | 32531fa | Commit Preview URL Branch Preview URL |
Jul 13 2026, 12:28 AM |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
uploads-auth | 32531fa | Commit Preview URL Branch Preview URL |
Jul 13 2026, 12:28 AM |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
uploads-api | 8b99db8 | Commit Preview URL Branch Preview URL |
Jul 13 2026, 12:40 AM |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
apps/web/src/pages/device.astro (1)
20-20: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winDrop
'unsafe-inline'fromscript-srcby movingauthOriginout of thedefine:varsinline block;CF_RUM_SCRIPT_SRCis just an external host allowlist, so it doesn’t need inline script support.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/pages/device.astro` at line 20, Update the device page’s script security policy to remove 'unsafe-inline' from script-src, and refactor the authOrigin usage so it is no longer passed through the inline define:vars block. Preserve authOrigin functionality using a non-inline mechanism while retaining CF_RUM_SCRIPT_SRC as the external script allowlist.apps/api/src/routes/tokens.ts (1)
31-31: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winFactor out the shared workspace-name regex
This is duplicated inapps/api/src/workspace.ts,apps/api/src/routes/admin.ts, and the workspace scripts. A single shared helper/constant would keep workspace-name validation from drifting.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api/src/routes/tokens.ts` at line 31, Extract the workspace-name pattern from the local WS_NAME_RE declaration into a shared helper or constant, then update the validation code in tokens.ts, workspace.ts, admin.ts, and the workspace scripts to reuse it. Remove duplicated regex definitions while preserving the existing validation behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/api/src/index.ts`:
- Around line 61-65: Apply writeRateLimit to the POST /v1/tokens route in the
tokens handler, using a rate-limit key derived from grant.workspace. Ensure the
workspace-specific key is used before token minting so each tenant’s minting
capacity is isolated.
In `@apps/api/src/routes/tokens.ts`:
- Around line 117-191: Add per-workspace throttling to the POST handler after
parseMintRequest produces grant, invoking the existing allowWrite guard with
c.env and grant.workspace before token creation. Preserve the current validation
and workspace authorization flow, and ensure a rate-limit rejection prevents
minting.
---
Nitpick comments:
In `@apps/api/src/routes/tokens.ts`:
- Line 31: Extract the workspace-name pattern from the local WS_NAME_RE
declaration into a shared helper or constant, then update the validation code in
tokens.ts, workspace.ts, admin.ts, and the workspace scripts to reuse it. Remove
duplicated regex definitions while preserving the existing validation behavior.
In `@apps/web/src/pages/device.astro`:
- Line 20: Update the device page’s script security policy to remove
'unsafe-inline' from script-src, and refactor the authOrigin usage so it is no
longer passed through the inline define:vars block. Preserve authOrigin
functionality using a non-inline mechanism while retaining CF_RUM_SCRIPT_SRC as
the external script allowlist.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 77287ae4-8340-42fc-a19c-51d4c34d8f34
📒 Files selected for processing (19)
.changeset/cli-device-login.mdapps/api/migrations/20260712230000_token_minting_user.sqlapps/api/src/auth-db.tsapps/api/src/index.tsapps/api/src/org-workspaces.tsapps/api/src/routes/me.tsapps/api/src/routes/tokens.test.tsapps/api/src/routes/tokens.tsapps/api/test/routes-galleries.test.tsapps/auth/migrations/20260712230000_device_code.sqlapps/auth/src/auth.tsapps/auth/src/device.test.tsapps/auth/src/schema.tsapps/web/src/lib/auth-client.tsapps/web/src/pages/device.astropackages/uploads/src/cli.tspackages/uploads/src/client.tspackages/uploads/src/commands/login.tspackages/uploads/test/commands-login.test.ts
Addresses CodeRabbit review on #112. - POST /v1/tokens now runs the WRITE_LIMITER (via allowWrite) keyed by the grant's workspace, after the membership gate so a non-member can't burn a workspace's mint budget. Minting is now throttled like other mutating routes. - Export WS_NAME_RE from workspace.ts and reuse it in the token-mint route instead of a third local copy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 4 of the Better Auth plan — CLI device login. Closes #101.
uploads loginnow signs in through a browser by default (RFC 8628 device flow), mints a workspace token from the resulting session, and saves it — no enrollment code to paste. The--codeenrollment path is retained.What's included
apps/auth
deviceAuthorization+bearerplugins, with a fail-closedvalidateClientallowlisting only the staticuploads-cliclient id.device_codetable + migration (20260712230000_device_code.sql).schema: {}zod workaround is unnecessary on better-auth 1.6.23 (itsschemaoption is already.optional()).apps/api
POST /v1/tokens— session/bearer-authenticated mint over theAUTHservice binding. Request is agrantsarray (forward-compat) with single-grant validation; gated on KV workspace existence + org membership; mints via the existingcreateToken.GET /v1/tokens— lists the caller's mintable workspaces so the CLI auto-selects when there's exactly one.minting_user_idcolumn onauth_tokens(nullable migration) recorded on each mint.apps/web
/deviceapproval page (session-gated approve/deny), plus device helpers inauth-client.ts.CLI (
@buildinternet/uploads)device/code→ open browser → polldevice/token(honorsauthorization_pending/slow_down/expired_token) → mint → save.--non-interactivewith no code fails fast rather than hanging. Changeset included.Notes for review
/simplifymulti-agent pass were run before this PR; both fed fixes back in (fail-fast non-interactive guard, 503-on-outage instead of a misleading 403, sharedmembershipsForUser,loadWorkspaceRecordreuse, parallelized lookups).apps/auth/src/device.test.ts./devicepage couldn't be screenshotted here — it needs the live auth worker + GitHub OAuth to render past the "checking session" state.device_coderow cleanup is deferred to Phase 5 (per the plan); the plugin already deletes expired/denied codes on access.Verification
pnpm types,pnpm check(oxlint + oxfmt), and all suites green: auth 71, api 205, cli 165, web 17, mcp 22, storage 19, email 8.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
uploads login.--workspacewhen multiple workspaces are available.--codeand--code-stdin.Bug Fixes