Skip to content
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,21 @@ codex auth fix --dry-run
codex auth doctor --fix
```

If the shell should not launch a browser, use the manual callback flow:

```bash
codex auth login --manual
CODEX_AUTH_NO_BROWSER=1 codex auth login
```
Comment on lines +115 to +117
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

document the windows syntax for CODEX_AUTH_NO_BROWSER.

lib/auth/browser.ts:25-32 makes CODEX_AUTH_NO_BROWSER a cross-platform toggle, and the suppression behavior is covered in test/browser.test.ts:76-82. the example here is posix-only, so windows users copying it from powershell or cmd will not actually set the variable. please add a powershell example here, and mirror it in docs/getting-started.md:61-64.

as per coding guidelines, docs/**: keep README, SECURITY, and docs consistent with actual CLI flags and workflows. whenever behavior changes, require updated upgrade notes and mention new npm scripts.

suggested fix
 codex auth login --manual
 CODEX_AUTH_NO_BROWSER=1 codex auth login
+$env:CODEX_AUTH_NO_BROWSER="1"; codex auth login
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 115 - 117, Add explicit Windows examples for setting
the CODEX_AUTH_NO_BROWSER env var to README.md and mirror them in
docs/getting-started.md: show both PowerShell and cmd ways to set
CODEX_AUTH_NO_BROWSER (matching the cross-platform toggle implemented in
lib/auth/browser.ts and the suppression behavior verified in
test/browser.test.ts) and ensure the CLI examples after setting the var reflect
the same `codex auth login` flows so README, docs, and tests remain consistent.


In non-TTY/manual shells, provide the full redirect URL on stdin instead of waiting for a browser callback:

```bash
echo "http://127.0.0.1:1455/auth/callback?code=..." | codex auth login --manual
```
Comment on lines +119 to +123
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

do not teach users to put the callback url on the command line.

echo "http://127.0.0.1:1455/auth/callback?code=..." | codex auth login --manual puts the live oauth code into shell history and makes it visible in process listings. lib/codex-manager.ts:1132-1207 already accepts stdin input, so the safer doc pattern is “feed the callback url on stdin from a secure source” rather than showing it as a command-line argument. the same example appears in docs/getting-started.md:66-70.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 119 - 123, The README example instructs users to echo
the callback URL on the command line (which leaks secrets via shell history and
process listings); update the example to show feeding the callback URL on stdin
from a secure source (e.g., pipe the URL from a protected file or clipboard
helper into the CLI) instead of using echo with the URL literal. Change the line
in README.md and the duplicate in docs/getting-started.md to a secure stdin
pattern and note that lib/codex-manager.ts (accepting stdin at the handler
around lines 1132-1207) will read the callback from stdin when using `codex auth
login --manual`.


No new npm scripts or storage migration steps are required for this login-flow update.

---

## Command Toolkit
Expand Down Expand Up @@ -234,6 +249,7 @@ codex auth login
- `codex auth` unrecognized: run `where codex`, then follow `docs/troubleshooting.md` for routing fallback commands
- Switch succeeds but wrong account appears active: run `codex auth switch <index>`, then restart session
- OAuth callback on port `1455` fails: free the port and re-run `codex auth login`
- Browser launch is blocked or you are in a headless shell: re-run `codex auth login --manual` or set `CODEX_AUTH_NO_BROWSER=1`
- `missing field id_token` / `token_expired` / `refresh_token_reused`: re-login affected account

</details>
Expand Down
4 changes: 3 additions & 1 deletion docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ User-facing capability map for `codex-multi-auth`.
| Quick switch and search hotkeys | Faster navigation in the dashboard |
| Account action hotkeys | Per-account set, refresh, toggle, and delete shortcuts |
| In-dashboard settings hub | Runtime and display tuning without editing files directly |
| Browser-first OAuth with manual fallback | Works in normal and constrained terminal environments |
| Browser-first OAuth with manual fallback | `codex auth login` stays browser-first, while `--manual`, `--no-browser`, and `CODEX_AUTH_NO_BROWSER=1` keep login usable in browser-restricted shells |

Manual/non-TTY login accepts the full callback URL on stdin, so automation and host-managed shells can complete auth without relying on a local browser handoff.
Comment on lines +57 to +59
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

mention callback-listener failures here too.

lib/codex-manager.ts:1540-1561 skips startLocalOAuthServer() in manual mode, and the bind-failure fallback is already exercised in test/codex-manager-cli.test.ts:3043. this row should mention localhost callback-listener failures as well, not only browser-restricted shells, otherwise the feature map under-documents the flow this pr adds.

as per coding guidelines, docs/**: keep README, SECURITY, and docs consistent with actual CLI flags and workflows. whenever behavior changes, require updated upgrade notes and mention new npm scripts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/features.md` around lines 57 - 59, The docs row about "Browser-first
OAuth with manual fallback" omits localhost callback-listener bind failures;
update docs/features.md to mention that manual/non-TTY login also covers local
callback-listener bind failures (when startLocalOAuthServer() is skipped in
manual mode in lib/codex-manager.ts and the bind-failure fallback exercised by
test/codex-manager-cli.test.ts:3043), and add/update corresponding upgrade notes
and README/SECURITY/docs entries to reflect the changed CLI workflows, flags
(`--manual`, `--no-browser`, CODEX_AUTH_NO_BROWSER) and any new npm scripts
introduced by this PR.


---

Expand Down
16 changes: 16 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,21 @@ codex auth list
codex auth check
```

If browser launch is blocked or you want to handle the callback manually:

```bash
codex auth login --manual
CODEX_AUTH_NO_BROWSER=1 codex auth login
```

In non-TTY/manual shells, provide the full redirect URL on stdin:

```bash
echo "http://127.0.0.1:1455/auth/callback?code=..." | codex auth login --manual
```

`codex auth login` remains browser-first by default. No new npm scripts or storage migration steps are required for this auth-flow update.

---

## Add More Accounts
Expand Down Expand Up @@ -111,6 +126,7 @@ If the OAuth callback on port `1455` fails:

- stop the process using port `1455`
- rerun `codex auth login`
- if browser launch is unavailable, rerun `codex auth login --manual`

If account state looks stale:

Expand Down
13 changes: 13 additions & 0 deletions docs/reference/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Compatibility aliases are supported:

| Flag | Applies to | Meaning |
| --- | --- | --- |
| `--manual`, `--no-browser` | login | Skip browser launch and use manual callback flow |
| `--json` | verify-flagged, forecast, report, fix, doctor | Print machine-readable output |
| `--live` | forecast, report, fix | Use live probe before decisions/output |
| `--dry-run` | verify-flagged, fix, doctor | Preview without writing storage |
Expand All @@ -55,11 +56,23 @@ Compatibility aliases are supported:

---

## Upgrade Notes

- `codex auth login` remains browser-first by default.
- `codex auth login --manual` and `codex auth login --no-browser` force the manual callback flow instead of launching a browser.
- `CODEX_AUTH_NO_BROWSER=1` suppresses browser launch for automation/headless sessions. False-like values such as `0` and `false` do not disable browser launch by themselves.
- In non-TTY/manual shells, pass the full redirect URL on stdin, for example: `echo "http://127.0.0.1:1455/auth/callback?code=..." | codex auth login --manual`.
- No new npm scripts or storage migration steps were introduced for this auth-flow update.

---
Comment on lines +59 to +67
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

documentation phrasing may mislead operators on env precedence.

line 63 says "False-like values such as 0 and false do not disable browser launch by themselves."

per lib/auth/browser.ts:18-21, when CODEX_AUTH_NO_BROWSER is set to any non-empty value (including "0"), the function returns early:

if (explicitNoBrowser.length > 0) {
  return NO_BROWSER_TRUTHY_VALUES.has(explicitNoBrowser);
}

this means CODEX_AUTH_NO_BROWSER="0" returns false (browser allowed) and skips the BROWSER check entirely.

result: CODEX_AUTH_NO_BROWSER="0" + BROWSER="none" → browser launches (confirmed by test/browser.test.ts:108-136).

consider clarifying: "Setting CODEX_AUTH_NO_BROWSER to any value (including 0 or false) overrides the BROWSER environment variable check."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/reference/commands.md` around lines 59 - 67, Update the phrasing in the
Upgrade Notes to state that setting CODEX_AUTH_NO_BROWSER to any non-empty value
(including "0" or "false") overrides the BROWSER environment check and
short-circuits the logic in lib/auth/browser.ts (the early return that uses
explicitNoBrowser and NO_BROWSER_TRUTHY_VALUES), so operators know
CODEX_AUTH_NO_BROWSER takes precedence (see related behavior covered by
test/browser.test.ts).


## Compatibility and Non-TTY Behavior

- `codex` remains the primary wrapper entrypoint. It routes `codex auth ...` and the compatibility aliases to the multi-auth runtime, and forwards every other command to the official `@openai/codex` CLI.
- In non-TTY or host-managed sessions, including `CODEX_TUI=1`, `CODEX_DESKTOP=1`, `TERM_PROGRAM=codex`, or `ELECTRON_RUN_AS_NODE=1`, auth flows degrade to deterministic text behavior.
- The non-TTY fallback keeps `codex auth login` predictable: it defaults to add-account mode, skips the extra "add another account" prompt, and auto-picks the default workspace selection when a follow-up choice is needed.
- `codex auth login --manual` keeps the login flow usable in browser-restricted shells by printing the OAuth URL and accepting manual callback input instead of trying to open a browser.
- In non-TTY/manual shells, provide the full redirect URL on stdin, for example: `echo "http://127.0.0.1:1455/auth/callback?code=..." | codex auth login --manual`.

---

Expand Down
12 changes: 12 additions & 0 deletions docs/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ codex auth forecast --live --model gpt-5-codex

---

## Login Flow Upgrade Notes

- `codex auth login` remains the default browser-first path.
- `codex auth login --manual` and `codex auth login --no-browser` force manual callback handling for browser-restricted shells.
- `CODEX_AUTH_NO_BROWSER=1` suppresses browser launch for automation/headless sessions. False-like values such as `0` and `false` no longer force manual mode.
- In non-TTY/manual shells, provide the full redirect URL on stdin, for example: `echo "http://127.0.0.1:1455/auth/callback?code=..." | codex auth login --manual`.
- No new npm scripts, storage migrations, or extra upgrade steps were introduced for this auth-flow change.

For the full command/behavior reference, see [reference/commands.md](reference/commands.md).

---

## Configuration Upgrade Notes

During upgrades, runtime config source precedence is:
Expand Down
5 changes: 3 additions & 2 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
REDIRECT_URI,
} from "./lib/auth/auth.js";
import { queuedRefresh } from "./lib/refresh-queue.js";
import { openBrowserUrl } from "./lib/auth/browser.js";
import { isBrowserLaunchSuppressed, openBrowserUrl } from "./lib/auth/browser.js";
import { startLocalOAuthServer } from "./lib/auth/server.js";
import { promptAddAnotherAccount, promptLoginMode } from "./lib/cli.js";
import {
Expand Down Expand Up @@ -2386,9 +2386,10 @@ while (attempted.size < Math.max(1, accountCount)) {

const accounts: TokenSuccessWithAccount[] = [];
const noBrowser =
inputs?.manual === "true" ||
inputs?.noBrowser === "true" ||
inputs?.["no-browser"] === "true";
const useManualMode = noBrowser;
const useManualMode = noBrowser || isBrowserLaunchSuppressed();
const explicitLoginMode =
inputs?.loginMode === "fresh" || inputs?.loginMode === "add"
? inputs.loginMode
Expand Down
17 changes: 17 additions & 0 deletions lib/auth/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import fs from "node:fs";
import path from "node:path";
import { PLATFORM_OPENERS } from "../constants.js";

const BROWSER_DISABLED_VALUES = new Set(["0", "false", "no", "off", "none"]);
const NO_BROWSER_TRUTHY_VALUES = new Set(["1", "true", "yes", "on"]);

/**
* Gets the platform-specific command to open a URL in the default browser
* @returns Browser opener command for the current platform
Expand All @@ -19,6 +22,16 @@ export function getBrowserOpener(): string {
return PLATFORM_OPENERS.linux;
}

export function isBrowserLaunchSuppressed(): boolean {
const explicitNoBrowser = (process.env.CODEX_AUTH_NO_BROWSER ?? "").trim().toLowerCase();
if (explicitNoBrowser.length > 0) {
return NO_BROWSER_TRUTHY_VALUES.has(explicitNoBrowser);
}

const browserSetting = (process.env.BROWSER ?? "").trim().toLowerCase();
return BROWSER_DISABLED_VALUES.has(browserSetting);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Determines whether a given command name exists on the system PATH.
*
Expand Down Expand Up @@ -92,6 +105,10 @@ function commandExists(command: string): boolean {
*/
export function openBrowserUrl(url: string): boolean {
try {
if (isBrowserLaunchSuppressed()) {
return false;
}

// Windows: use PowerShell Start-Process to avoid cmd/start quirks with URLs containing '&' or ':'
if (process.platform === "win32") {
if (!commandExists("powershell.exe")) {
Expand Down
Loading