Skip to content

[WEBXP-469] Add circleci signup command — Magic Path v2#1199

Open
Fab10-CircleCi wants to merge 1 commit intoCircleCI-Public:mainfrom
Fab10-CircleCi:fabio/webxp-469-magic-path-cli-v2
Open

[WEBXP-469] Add circleci signup command — Magic Path v2#1199
Fab10-CircleCi wants to merge 1 commit intoCircleCI-Public:mainfrom
Fab10-CircleCi:fabio/webxp-469-magic-path-cli-v2

Conversation

@Fab10-CircleCi
Copy link
Copy Markdown

Checklist

=========

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have checked for similar issues and haven't found anything relevant.
  • This is not a security issue (which should be reported here: https://circleci.com/security/)
  • I have read Contribution Guidelines.

Internal Checklist

  • I am requesting a review from my own team as well as the owning team
  • I have a plan in place for the monitoring of the changes that I am making (this can include new monitors, logs to be aware of, etc...)

Changes

=======

  • Add newSignupCommand to cmd/signup.go — new circleci signup command with hybrid browser flow
  • Add corsMiddleware to cmd/signup.go — CORS + Origin validation + Chrome Private Network Access headers
  • Add handleToken to cmd/signup.go — localhost /token endpoint with constant-time state validation and JSON responses
  • Add generateState to cmd/signup.go — 128-bit cryptographic nonce via crypto/rand
  • Add stateMatches to cmd/signup.go — constant-time comparison via crypto/subtle
  • Add --no-browser flag for headless/SSH environments (manual PAT paste)
  • Add --force flag to bypass already-authenticated guard
  • Add unique PAT label generation (circleci-cli-HOSTNAME-TIMESTAMP) to prevent 422 duplicate errors
  • Register signup command in cmd/root.go
  • Update subcommand count in cmd/root_test.go (29 → 30)
  • Add 24 unit tests in cmd/signup_unit_test.go

Rationale

=========

This implements the CLI side of the "Magic Path" hybrid signup flow (WEBXP-469). The goal: run circleci signup, complete signup (or authenticate) in the browser, and have the CLI automatically configured — no manual token copy-paste.

The CLI opens the browser directly to app.circleci.com/cli-auth with CLI params as top-level query params. The frontend handles session detection:

  • Authenticated users → instant PAT creation (~2 seconds, zero clicks)
  • New users → redirect to signup, then back for PAT creation

This is "Magic Path v2" — v1 routed through /authentication/signup/ where auth-svc drops the return-to parameter for already-authenticated users. v2 bypasses auth-svc entirely by going directly to the frontend application page.

Greenfield — no existing commands or behaviors are modified. The setup command is untouched.

Considerations

==============

  • Why /cli-auth instead of /authentication/signup/? Auth-svc has a bug where it drops return-to for already-authenticated users, causing the CLI to time out. Going directly to a frontend page bypasses this entirely.
  • Why strict Origin validation (403)? The localhost server receives auth tokens. Defense-in-depth: even though the browser enforces CORS, the server rejects requests from non-app.circleci.com origins at the application level.
  • Why crypto/subtle for state comparison? Prevents timing-based attacks on the cli_state nonce. Practical risk is low (localhost, 128-bit nonce), but it's a best practice for secret comparison.
  • Why Access-Control-Allow-Private-Network: true? Chrome's Private Network Access policy blocks fetch() from public websites to localhost without this header. Confirmed in production testing.
  • Why unique PAT labels? POST /api/v1/user/token returns 422 if a token with the same label exists. Users running signup from multiple machines or re-running after config deletion would hit this. circleci-cli-HOSTNAME-TIMESTAMP prevents collisions.
  • Why JSON responses? The frontend's fetch() benefits from structured responses ({"status":"ok"} / {"status":"error"}) for error handling and future extensibility.

Screenshots

============

Before

N/A — new command, no prior behavior.

After

$ circleci signup
Opening browser to complete signup...
  https://app.circleci.com/cli-auth?cli_port=62302&cli_state=5c865a25...&cli_label=circleci-cli-MacBook-1775022542
Waiting for authentication...

✅ Welcome to CircleCI! Your CLI is now authenticated.
$ circleci signup
You're already authenticated. Your CLI is configured with a personal API token.
If you want to reconfigure, run `circleci setup`.

🤖 Generated with Claude Code

Opens browser directly to /cli-auth with CLI params as top-level
query params. The frontend handles session detection: authenticated
users get instant PAT creation; unauthenticated users are redirected
to signup first. Bypasses auth-svc return-to bug entirely.

Security hardening:
- Strict Origin validation (reject missing/wrong with 403)
- Constant-time state comparison via crypto/subtle
- CORS pinned to https://app.circleci.com (static, never reflected)
- Access-Control-Allow-Private-Network for Chrome PNA
- Method validation (GET only on /token)
- 127.0.0.1 binding only

Features:
- Unique PAT label (hostname + timestamp) prevents 422 duplicates
- Already-authenticated guard with --force bypass
- --no-browser fallback for headless/SSH
- JSON responses for structured error handling
- Human-readable URL display in terminal
- 24 unit tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Fab10-CircleCi Fab10-CircleCi force-pushed the fabio/webxp-469-magic-path-cli-v2 branch from 6db9f76 to 352a958 Compare April 2, 2026 07:20
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