Skip to content

feat(tui,cli): provider → auth-method login chooser + profile-picker fixes (cursor-reset, hover shows provider/weekly-left)#362

Merged
andrei-hasna merged 1 commit into
mainfrom
feat/auth-login-provider-method-chooser
Jul 22, 2026
Merged

feat(tui,cli): provider → auth-method login chooser + profile-picker fixes (cursor-reset, hover shows provider/weekly-left)#362
andrei-hasna merged 1 commit into
mainfrom
feat/auth-login-provider-method-chooser

Conversation

@andrei-hasna

Copy link
Copy Markdown
Contributor

What & why

The "login with another profile" flow dead-ended into a single login page from both entry points, and the profile picker had two UX bugs. This adds a consistent two-level, data-driven chooser — provider → login method (only the methods that provider supports) — and fixes the picker.

The two entry points, fixed

1. codewith --auth-profile <name> for a profile that doesn't exist.
Previously the app scoped auth to the empty profile dir → NotAuthenticated → dropped you into the generic onboarding login with no provider choice (and silently created the profile). Now: codex-login::auth_profile_exists gates it; a missing profile falls back to your default login (no dead-end) and auto-opens the provider chooser to create the named profile. The --auth-profile selection resolution/env-scoping decision is a small pure function (auth_profile_env_decision) with unit tests.

2. /profile → "Log in new profile".
Previously showed a hardcoded ChatGPT/Cursor/Grok list (Claude.ai was dropped) and, for ChatGPT, jumped straight to browser OAuth with no method choice. Now it's a two-step selector:

  Choose provider
  Pick who this profile signs in with; you'll choose a login method next.

› 1. ChatGPT    Create a profile tied to ChatGPT.
  2. Claude.ai  Tie this profile to your Claude.ai (Anthropic) subscription login.
  3. Cursor     Tie this profile to your Cursor subscription login.
  4. Grok       Tie this profile to your Grok subscription login.

then, for the chosen provider, only its supported methods:

  Choose login method
  Sign-in methods for ChatGPT.

› 1. Browser sign-in  Open a browser and sign in with your ChatGPT account.
  2. Device code      Sign in on another device using a short code.
  3. API key          Finish with an API key via the `codewith login` CLI.

Single-method providers (Claude.ai/Cursor/Grok today) skip the method step.

Data-driven (no hardcoded provider/method lists)

The chooser derives everything from a new capability model in codex-login — the single source of truth:

  • AuthProfileSubscriptionProvider::ALL, description()
  • AuthProfileLoginMethod + supported_login_methods()

Iterating ALL is what restores Claude.ai automatically. The TUI filters methods by forced_login_method. Method selection routes to the existing login flows: browser OAuth runs in-session; device-code and API-key hand off to the codewith login CLI so a credential never passes through the TUI (the composer prompt has no masking — deliberately not capturing secrets on-screen).

Bug B — cursor reset while navigating /profile

A background usage/rate-limit heartbeat rebuilt the popup mid-navigation and snapped the highlight back to the active profile (the is_current auto-select beat the passed cursor index). Fix: a new opt-in SelectionViewParams.prefer_initial_over_current; the in-place refresh sets it so the rebuild honors the user's cursor. First-open and every other popup keep the historic is_current behavior (scoped, zero blast radius).

Bug C — hover/detail showed the email

The highlighted profile's detail now shows the subscription provider + weekly usage remaining, never the account email. When weekly usage isn't loaded yet it shows a placeholder (provider is always visible):

  1. default             Root login
› 2. personal            ChatGPT · weekly 60% left     ← loaded
› 2. personal            ChatGPT · usage loading…      ← not yet loaded
  3. work                ChatGPT API key

Weekly-left is read from the /usage rate-limit data (weekly window identified via usage_profile_health::WEEKLY_LIMIT_LABEL); the email was removed from the always-visible row description too.

Tests

New/updated: capability model + exhaustiveness (codex-login), auth_profile_exists, cursor-preservation-across-refresh, provider+weekly-detail, the missing-profile routing decision, two-step chooser flow, and the API-only/duplicate/external-provider paths. Insta snapshots regenerated for the provider chooser, the new method chooser, and the three picker screens (reviewed — no secrets).

Blacksmith result

Clean checkout of this commit on a Blacksmith 16-vCPU box: cargo nextest run -p codex-login -p codex-tui3607 passed, 0 failed, 4 skipped. Nightly cargo fmt --check --config imports_granularity=Item clean. Staged secrets scan clean.

Deferred follow-ups

  • In-session device-code (renders a URL + short code; no secret) and a masked API-key entry for named profiles — currently handed off to the CLI.
  • Entry point 1: fully presenting the provider chooser inside onboarding (before the app starts) for the unauthenticated-root case; today it falls back to root + auto-opens the chooser after startup. Requires reordering the pre-app-server auth scoping + onboarding gate.
  • The default/root picker row still shows the compact usage hint; unifying it with the provider+weekly format is cosmetic.

…fixes

Add a two-level, data-driven login chooser (provider → login method) reachable
from both auth-profile entry points, and fix two profile-picker bugs.

- Capability model in codex-login as the single source of truth (no hardcoded
  provider/method lists): AuthProfileSubscriptionProvider::ALL, description(),
  AuthProfileLoginMethod, supported_login_methods(). This alone restores Claude.ai,
  which the old hardcoded chooser dropped.
- /profile -> "Log in new profile" now shows a provider chooser (every provider),
  then a per-provider method chooser listing only supported methods (filtered by
  forced_login_method); single-method providers skip the extra step. Methods route
  to existing flows: browser OAuth in-session; device-code and API-key hand off to
  the `codewith login` CLI so secrets never pass through the TUI.
- `codewith --auth-profile <missing>` no longer dead-ends in a login screen: it
  falls back to the default login and opens the provider chooser to create the
  named profile (gated by codex-login::auth_profile_exists).
- Fix profile-picker cursor reset (Bug B): the in-place popup refresh opts into a
  new SelectionViewParams.prefer_initial_over_current so a background usage-heartbeat
  rebuild keeps the user's cursor instead of snapping back to the active-profile row.
- Profile-picker highlighted-row detail shows the subscription provider + weekly
  usage remaining (loading placeholder until fetched), never the account email (Bug C).

Tests cover the capability model, auth_profile_exists, cursor preservation, the
provider+weekly detail, and the missing-profile routing decision; flow tests and
insta snapshots updated. Full codex-login + codex-tui suites pass on Blacksmith.
@andrei-hasna
andrei-hasna force-pushed the feat/auth-login-provider-method-chooser branch from ef4f2dc to bd554ba Compare July 22, 2026 09:01
@andrei-hasna
andrei-hasna merged commit 941a6b2 into main Jul 22, 2026
25 of 27 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 22, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant