Repository navigation
feat(cli): fixed callback port for browser login inside containers #5066
Description
User Story
As a user running the OpenShell CLI inside a container (for example through the hypershell installer's wrapper),
I want the CLI's browser-login callback listener to bind a fixed, published port,
so that I can complete browser-based gateway login from the host browser while the CLI runs in the container.
Problem Statement
Both CLI browser login flows — the OIDC Authorization Code + PKCE flow (oidc_browser_auth_flow) and the edge/mTLS browser flow (browser_auth_flow) — bind their local callback listener to a random ephemeral port on 127.0.0.1. A container wrapper cannot know a randomly chosen port before the CLI starts, so it cannot publish the port to the host. The host browser is then redirected to a callback URL that nothing forwards, and login hangs until timeout.
Impact / Why This Matters
Without this, browser login is effectively unusable when the CLI runs in a container. Today users must either run the CLI on the host, or use --network=host, which works on Linux but not with a podman machine VM on macOS, or fall back to non-browser flows. That blocks containerized CLI distributions (such as the hypershell installer) from supporting OIDC or edge gateways that require browser login, and forces the wrapper to choose platform-specific networking hacks instead of a portable port publish.
Proposed Design
Expose the callback listener controls through environment variables, used by both browser login flows:
OPENSHELL_CALLBACK_PORT: when set, the listener binds this fixed port instead of an ephemeral one, so the wrapper can publish-p 127.0.0.1:<port>:<port>before login starts. The value is validated as an integer from 1 to 65535.OPENSHELL_CALLBACK_BIND_ADDRESS: when set, the listener binds this address instead of127.0.0.1. Container runtimes with user-mode networking (pasta, slirp4netns) can only reach listeners bound to0.0.0.0, so a wrapper sets this inside the container while publishing the port to host loopback only.
Observable behavior:
- With both variables unset, the listener binds
127.0.0.1:0exactly as today; no default changes. - The bind address never widens to
0.0.0.0implicitly — only when explicitly set — since a wildcard bind on a host machine would expose the short-lived callback server to the local network. - The browser-facing redirect URL stays
http://127.0.0.1:<port>/callback(and the edge flow'scallback_portparameter uses the bound port), so identity-provider allowlists such as Keycloak'shttp://127.0.0.1:*keep matching without provider changes. - An invalid port or address value, or a fixed port already in use, fails the login command with a clear error instead of falling back silently.
Acceptance Criteria
- With both environment variables unset, both browser login flows bind
127.0.0.1on an ephemeral port, unchanged from current behavior. - With
OPENSHELL_CALLBACK_PORTset, both flows bind exactly that port; the redirect URL /callback_portreflect it. - With
OPENSHELL_CALLBACK_BIND_ADDRESS=0.0.0.0set, the listener binds all interfaces while the browser-facing URL remainshttp://127.0.0.1:<port>/callback. - Invalid values (non-integer, 0, out-of-range port; non-IP address) and a busy fixed port produce clear errors naming the offending variable.
- Environment variables are documented in the CLI auth reference.
Alternatives Considered
--network=hoston the container: works on Linux, but not with a podman machine VM on macOS, so it is not portable.- Deriving the bind address from a heuristic (e.g. detecting a container environment and auto-binding
0.0.0.0): rejected because silently widening the bind address on a host machine would be a security regression; the wildcard bind must stay an explicit operator choice. - A single variable combining port and address: rejected as less composable and harder to document; separate variables keep the default-safe behavior of each control independent.
Agent Investigation
- Both flows bind via
TcpListener::bind("127.0.0.1:0")incrates/openshell-cli/src/oidc_auth.rsandcrates/openshell-cli/src/auth.rs; the port used in the redirect URL is derived from the bound listener, so a shared bind helper covers both call sites. - Keycloak's redirect allowlist already covers
http://127.0.0.1:*, so no identity-provider change is needed. - Implementation exists in feat(cli): support fixed callback port for browser login in containers #5065 for review.
Activity
TaylorMutch commentedon Oct 30, 2026
Independent assessment: the reported limitation is confirmed by code inspection, and I recommend maintainer acceptance of the feature in principle, subject to the usage clarification below.
Both
browser_auth_flow(crates/openshell-cli/src/auth.rs) andoidc_browser_auth_flow(crates/openshell-cli/src/oidc_auth.rs) bind127.0.0.1:0. The OIDC redirect URI and the edge callback destination use IPv4 loopback and the listener's allocated port. A container's loopback listener is not reached by ordinary port publication, and the ephemeral port cannot be pinned in an advance mapping. Explicit fixed-port and bind-address controls address both constraints without changing defaults. Gateway middleware, interceptors, and credential providers cannot configure this CLI-owned listener.An existing alternative deserves mention:
OPENSHELL_NO_BROWSER=1selects OIDC device authorization when the IdP supports/enables it (see #2793). That does not cover the edge browser flow or IdPs without device authorization, so it does not eliminate this use case. I found no equivalent existing issue in the related-issue search; #5065 already implements this request.Before completing evidence triage, please confirm that you personally run OpenShell in the described container workflow, and give the OpenShell version, container runtime/host OS, and affected login mode. The current User Story describes a specific workflow but does not explicitly establish first-hand use. A sanitized invocation and observed failure would make this concrete.(Human note: seems irrelevant; @TaylorMutch )Implementation/validation considerations for #5065:
::1while always advertising127.0.0.1does not work directly without an explicit IPv4-to-IPv6 forwarding arrangement. Restrict supported addresses or document/test the required mapping.Setting
state:needs-infofor the narrow first-hand usage evidence above. Technical feasibility is established; the acceptance/roadmap decision remains with maintainers.