Skip to content

feat: Better Auth Phase 4 — CLI device login (uploads login)#112

Merged
Zach Dunn (zachdunn) merged 5 commits into
mainfrom
claude/github-issue-101-e3c3e0
Jul 13, 2026
Merged

feat: Better Auth Phase 4 — CLI device login (uploads login)#112
Zach Dunn (zachdunn) merged 5 commits into
mainfrom
claude/github-issue-101-e3c3e0

Conversation

@zachdunn

@zachdunn Zach Dunn (zachdunn) commented Jul 13, 2026

Copy link
Copy Markdown
Member

Phase 4 of the Better Auth plan — CLI device login. Closes #101.

uploads login now 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 --code enrollment path is retained.

What's included

apps/auth

  • deviceAuthorization + bearer plugins, with a fail-closed validateClient allowlisting only the static uploads-cli client id.
  • device_code table + migration (20260712230000_device_code.sql).
  • Verified the releases schema: {} zod workaround is unnecessary on better-auth 1.6.23 (its schema option is already .optional()).

apps/api

  • POST /v1/tokens — session/bearer-authenticated mint over the AUTH service binding. Request is a grants array (forward-compat) with single-grant validation; gated on KV workspace existence + org membership; mints via the existing createToken.
  • GET /v1/tokens — lists the caller's mintable workspaces so the CLI auto-selects when there's exactly one.
  • minting_user_id column on auth_tokens (nullable migration) recorded on each mint.

apps/web

  • /device approval page (session-gated approve/deny), plus device helpers in auth-client.ts.

CLI (@buildinternet/uploads)

  • Device flow: device/code → open browser → poll device/token (honors authorization_pending / slow_down / expired_token) → mint → save. --non-interactive with no code fails fast rather than hanging. Changeset included.

Notes for review

  • Auth-sensitive — bearer/session verification and token minting. A fable advisory pass and a /simplify multi-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, shared membershipsForUser, loadWorkspaceRecord reuse, parallelized lookups).
  • The browser-approval path (claim → approve → token exchange) is covered end-to-end against the fake-D1 harness in apps/auth/src/device.test.ts.
  • The /device page couldn't be screenshotted here — it needs the live auth worker + GitHub OAuth to render past the "checking session" state.
  • Expired device_code row 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

    • Added browser-based device authorization for uploads login.
    • Automatically mints and saves a workspace token after approval.
    • Added workspace selection with --workspace when multiple workspaces are available.
    • Added a browser page for entering, approving, or denying device login requests.
    • Preserved enrollment-code login through --code and --code-stdin.
  • Bug Fixes

    • Improved validation and error handling for token creation, workspace access, and device authorization states.

Zach Dunn (zachdunn) and others added 4 commits July 12, 2026 19:50
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>
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one) (2)
  • coderabbit:review
  • review
🚫 Excluded labels (none allowed) (1)
  • wip

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1cf627ad-580b-4529-82f2-b96e0e9b8e2b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds RFC 8628 device authorization for uploads login, a browser approval page, session-authenticated workspace token minting, minting-user persistence, shared membership lookup, and CLI tests while retaining enrollment-code login.

Changes

Device Login and Token Minting

Layer / File(s) Summary
Device authorization backend
apps/auth/...
Adds device-code storage, Better Auth device and bearer plugins, CLI client allowlisting, and lifecycle tests.
Device approval page
apps/web/src/lib/auth-client.ts, apps/web/src/pages/device.astro
Adds device status lookup, sign-in, approval, denial, and terminal-state UI flows.
Workspace token API
apps/api/...
Adds session-authenticated workspace listing and token minting, validates grants, records the minting user, and shares membership lookup logic.
CLI device login
packages/uploads/..., .changeset/cli-device-login.md
Adds device-code polling, workspace selection, workspace token minting, credential persistence, and enrollment-code compatibility.
Supporting tests and migration coverage
apps/api/src/routes/tokens.test.ts, apps/auth/src/device.test.ts, packages/uploads/test/commands-login.test.ts
Covers authorization, validation, polling, workspace selection, token minting, and error handling.

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
Loading

Possibly related PRs

Suggested labels: coderabbit:review

Poem

A bunny logs in through a browser bright,
Approves a code by morning light.
Tokens hop from workspace to shell,
Saved safely where credentials dwell.
Enrollment codes still know the way.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: Better Auth Phase 4 device login for uploads.
Linked Issues check ✅ Passed The PR covers the required device auth, bearer allowlist, /device page, /v1/tokens flow, and CLI device-login replacement.
Out of Scope Changes check ✅ Passed The changes stay focused on the Phase 4 device-login and token-minting work, with tests and supporting refactors tied to that scope.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/github-issue-101-e3c3e0

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

@zachdunn

Copy link
Copy Markdown
Member Author

CodeRabbit (@coderabbitai) review

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 13, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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

@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.

Actionable comments posted: 2

🧹 Nitpick comments (2)
apps/web/src/pages/device.astro (1)

20-20: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Drop 'unsafe-inline' from script-src by moving authOrigin out of the define:vars inline block; CF_RUM_SCRIPT_SRC is 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 win

Factor out the shared workspace-name regex
This is duplicated in apps/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

📥 Commits

Reviewing files that changed from the base of the PR and between fe2d5f3 and 32531fa.

📒 Files selected for processing (19)
  • .changeset/cli-device-login.md
  • apps/api/migrations/20260712230000_token_minting_user.sql
  • apps/api/src/auth-db.ts
  • apps/api/src/index.ts
  • apps/api/src/org-workspaces.ts
  • apps/api/src/routes/me.ts
  • apps/api/src/routes/tokens.test.ts
  • apps/api/src/routes/tokens.ts
  • apps/api/test/routes-galleries.test.ts
  • apps/auth/migrations/20260712230000_device_code.sql
  • apps/auth/src/auth.ts
  • apps/auth/src/device.test.ts
  • apps/auth/src/schema.ts
  • apps/web/src/lib/auth-client.ts
  • apps/web/src/pages/device.astro
  • packages/uploads/src/cli.ts
  • packages/uploads/src/client.ts
  • packages/uploads/src/commands/login.ts
  • packages/uploads/test/commands-login.test.ts

Comment thread apps/api/src/index.ts
Comment thread apps/api/src/routes/tokens.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>
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.

Better Auth Phase 4: CLI device login (uploads login)

1 participant