Skip to content

Auth: CLI device-code login#732

Open
jaeyunha wants to merge 1 commit into
stagingfrom
issue-518-auth-cli-device-code-login
Open

Auth: CLI device-code login#732
jaeyunha wants to merge 1 commit into
stagingfrom
issue-518-auth-cli-device-code-login

Conversation

@jaeyunha

Copy link
Copy Markdown
Member

Summary

  • add OpenAPI device-code auth endpoints, DB grant persistence, numeric 6-digit user codes, one-time CLI PAT issuance with cli scope
  • add /auth/device approval/denial UI and update login-page CLI pairing copy
  • make expn login default to browser device flow while preserving --token pat_... fallback

Verification

  • pnpm --filter @namuh-eng/expn-cli test
  • pnpm run typecheck
  • pnpm run lint
  • node scripts/check-openapi-coverage.mjs
  • node scripts/check-migrations.mjs
  • make check (blocked: go binary is not installed in this lane image; failed at pnpm api:build)

Remaining

  • Run Go build/tests and Playwright approval/denial e2e in an environment with Go and the local stack available.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 268a64fada

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

CREATE TABLE IF NOT EXISTS device_auth_grant (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
device_code_hash text NOT NULL UNIQUE,
user_code varchar(6) NOT NULL UNIQUE,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid reserving every six-digit code forever

Because user_code is globally unique, every approved, denied, expired, or consumed device grant permanently removes one of only 1,000,000 possible CLI codes. reserveUserCode only checks active pending grants, so once the random generator picks any historical code the insert fails with a unique-constraint error and CreateDeviceCode returns 500; the collision rate grows quickly as login attempts accumulate. Use a partial uniqueness strategy for active grants or retry on historical-code conflicts.

Useful? React with 👍 / 👎.

r.Post("/device/code", h.CreateDeviceCode)
r.Post("/device/token", h.PollDeviceToken)
r.Get("/device/grant", h.GetDeviceGrant)
r.Post("/device/grant", h.UpdateDeviceGrant)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Route device grant updates through auth middleware

UpdateDeviceGrant performs a cookie-authenticated state change by calling BrowserSession, but this route is mounted with the public auth routes in router.go, so it skips the authMiddleware.Require path that applies the Origin/Referer CSRF check and workspace IP restrictions for other mutating browser-session requests. In browser contexts where the session cookie is sent to /api/auth/device/grant, a forged POST can approve or deny a device code without those protections; move this endpoint behind the protected middleware or apply the same checks here.

Useful? React with 👍 / 👎.

Comment thread apps/cli/src/run.ts
Comment on lines +564 to +565
const child = spawn(command, args, { detached: true, stdio: "ignore" });
child.unref();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle browser-launch spawn errors asynchronously

When xdg-open/open is missing or BROWSER points to a bad command, Node's spawn() reports ENOENT via the child process error event rather than throwing from this call. With no error listener, default expn login can terminate on an unhandled error instead of returning false and showing the manual copy/paste instructions, which is common in headless shells and minimal containers.

Useful? React with 👍 / 👎.

@jaeyunha

Copy link
Copy Markdown
Member Author

Controller disposition for current head 268a64f: validation-blocked; do not merge yet. Current blocker:

  • PR Auth: CLI device-code login #732 implements the API/CLI/device approval UI slice, but the linked Auth: CLI device-code login #518/spec-prep acceptance still requires Playwright approval + denial coverage and web/account-security revocation visibility tests before merge.
  • GitHub reports no CI checks for this head, so local focused checks are the only evidence so far.

Controller evidence:

  • PR is MERGEABLE against staging; diff is current-path API/CLI/web/OpenAPI/SDK/migration work.
  • Passed: pnpm --filter @namuh-eng/expn-sdk build, pnpm --filter @namuh-eng/expn-sdk typecheck, pnpm --filter @namuh-eng/expn-cli test, pnpm --filter @namuh-eng/expn-cli typecheck, node scripts/check-openapi-coverage.mjs, node scripts/check-migrations.mjs, pnpm api:build, pnpm api:test, focused Biome on touched files.
  • Baseline note: broad web Vitest selection is already failing on unrelated exponential.app/ vs localhost:7015/ workspace URL expectations; not a Auth: CLI device-code login #732 source failure.

Required before merge: add/run the missing device-flow Playwright approval+denial coverage and web/account-security revocation visibility coverage, then refresh this PR head.

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