fixing openai codex cli oauth authentication on remote machines
on this page
tl;dr
Problem: | ERR_CONNECTION_REFUSED on localhost:1455 after Codex login |
Cause: | OAuth callback can’t reach CLI on remote machine |
Solution: | SSH port forwarding: ssh -L 1455:localhost:1455 user@remote |
Affects: | OpenAI Codex CLI, any OAuth-based CLI tools on remote hosts |
Alternative: | Run CLI locally or wait for headless auth support |
the openai codex cli uses oauth authentication that redirects to localhost:1455
for callback handling. when running the cli on a remote machine accessed via ssh, this callback fails because your browser can’t reach the remote machine’s localhost port. this guide explains the problem and provides a working solution using ssh port forwarding.
the problem
when authenticating with the codex cli on a remote machine, you’ll encounter this workflow:
- run
codex login
on remote machine - cli opens browser with oauth url
- complete authentication on openai/chatgpt website
- browser redirects to
http://localhost:1455/auth/callback?code=...&scope=openid+profile+email+offline_access&state=...
- browser shows “connection refused” error - nothing is listening on your local port 1455
error symptoms
This site can't be reached
localhost refused to connect.
ERR_CONNECTION_REFUSED
the url in your browser will look like:
http://localhost:1455/auth/callback?code=abc123...&scope=openid+profile+email+offline_access&state=xyz789...
why it happens
the oauth flow expects the cli callback server to be running on the same machine as your browser:
- cli runs on: remote server (e.g.,
server.example.com
) - callback server listens on: remote’s
localhost:1455
- browser redirects to: your local machine’s
localhost:1455
- result: connection refused - no path between browser and remote cli
this affects common remote development scenarios:
- ssh sessions to development servers
- docker containers
- cloud vms and vps instances
- wsl environments accessed remotely
- codespaces and github.dev
- tmux sessions on remote hosts
- ci/cd runners
the solution: ssh port forwarding
ssh local port forwarding creates a tunnel from your local machine’s port 1455 to the remote machine’s port 1455, allowing the oauth callback to reach the cli.
basic setup
# connect with port forwarding
ssh -L 1455:localhost:1455 user@remote-server
# or add to existing connection
ssh -L 1455:localhost:1455 user@example.com
step-by-step process
-
establish ssh connection with port forwarding:
ssh -L 1455:localhost:1455 username@remote-host
-
run codex cli on remote machine:
codex login
-
complete browser authentication:
- browser opens authentication page
- log in with your openai/chatgpt account
- authorize the application
-
callback reaches remote cli:
- browser redirects to
localhost:1455/auth/callback
- ssh tunnel forwards to remote machine
- cli receives auth code and completes login
- browser redirects to
important: port conflict on local machine
if you try to run codex login
on your local machine while the ssh tunnel is active, you’ll get an error:
$ codex login
Error logging in: Address in use (os error 98)
this happens because port 1455 is already in use by the ssh tunnel. you must either:
- close the ssh connection first
- use a different ssh session without port forwarding for local login
- kill the ssh tunnel process
advanced configurations
background port forwarding
run port forwarding in background without interactive shell:
# start tunnel in background
ssh -f -N -L 1455:localhost:1455 user@remote
# run your commands locally that trigger remote oauth
# ...
# find and kill the background ssh when done
ps aux | grep "ssh -f -N"
kill <pid>
multiple ports
forward additional ports for other services:
ssh -L 1455:localhost:1455 \
-L 8080:localhost:8080 \
-L 3000:localhost:3000 \
user@remote
ssh config file
add to ~/.ssh/config
for permanent configuration:
Host myserver
HostName server.example.com
User myusername
LocalForward 1455 localhost:1455
then connect with:
ssh myserver
using different local port
if port 1455 is already in use locally:
# forward local 2455 to remote 1455
ssh -L 2455:localhost:1455 user@remote
# manually modify callback url in browser from :1455 to :2455
alternative solutions
1. reverse proxy with ngrok
expose the remote port publicly (security implications):
# on remote machine
ngrok http 1455
# use the ngrok url for callback (requires cli modification)
2. run cli locally
if possible, install and run the cli on your local machine:
# local installation
pip install codex-cli # or appropriate installation method
codex login
# note: close any ssh tunnels on port 1455 first
3. use api keys directly
bypass oauth entirely with api key authentication:
export OPENAI_API_KEY="sk-..."
# use cli without login
4. wait for headless authentication
there’s an open feature request for copy-paste auth flow that would eliminate this issue. the proposed solution would:
- display an auth url to visit
- show a code to copy
- paste code back to cli
- complete authentication without localhost callback
troubleshooting
port already in use
# check what's using port 1455
lsof -i :1455
# or
netstat -tlnp | grep 1455
# kill the process or use different port
kill <pid>
connection still refused
verify tunnel is active:
# on local machine
telnet localhost 1455
# should connect if tunnel is active
# check ssh process
ps aux | grep "ssh.*1455"
authentication completes but cli doesn’t respond
ensure cli is actually listening:
# on remote machine
netstat -tlnp | grep 1455
# should show codex process
multiple ssh sessions
each ssh session needs port forwarding:
# wrong - second session without forwarding
ssh user@remote # no -L flag
# right - all sessions forward the port
ssh -L 1455:localhost:1455 user@remote
security considerations
localhost-only binding
the default -L 1455:localhost:1455
only binds to your local loopback interface, preventing external access.
encrypted tunnel
all traffic through the ssh tunnel is encrypted, protecting the oauth tokens.
temporary forwarding
close the tunnel after authentication:
# authenticate
codex login
# verify success
codex --help # or appropriate test command
# exit ssh to close tunnel
exit
avoid public exposure
never use 0.0.0.0
or public ips for oauth callbacks:
# dangerous - exposes to network
ssh -L 0.0.0.0:1455:localhost:1455 user@remote # don't do this
common scenarios
vscode remote development
when using vscode remote-ssh:
-
add port forwarding in vscode:
- command palette: “forward a port”
- enter port: 1455
- local address: localhost:1455
-
or modify ssh command in settings:
{ "remote.SSH.defaultForwardedPorts": [ { "localPort": 1455, "remotePort": 1455 } ] }
docker containers
# run container with port mapping
docker run -it -p 1455:1455 myimage
# or with docker-compose
services:
app:
ports:
- '1455:1455'
tmux/screen sessions
port forwarding persists across tmux sessions:
# initial connection
ssh -L 1455:localhost:1455 user@remote
tmux
# detach and reattach - forwarding remains
# ctrl+b d (detach)
tmux attach
aws ec2 instances
# include port forwarding with ec2 connection
ssh -i ~/.ssh/mykey.pem -L 1455:localhost:1455 ec2-user@ec2-instance.amazonaws.com
checking port status
local machine
# mac
lsof -nP -iTCP:1455 -sTCP:LISTEN
# linux
ss -tlnp | grep 1455
# windows (powershell)
netstat -an | findstr :1455
remote machine
# check if cli is listening
netstat -tlnp | grep 1455
# should show something like:
# tcp 0 0 127.0.0.1:1455 0.0.0.0:* LISTEN 12345/codex
best practices
- use ssh config: define forwarding rules permanently
- close tunnels: exit ssh sessions after authentication
- verify success: test cli commands before closing tunnel
- document setup: add to project readme for team members
- consider alternatives: evaluate if remote cli is necessary
limitations
current issues
- no native headless authentication support
- requires ssh access to remote machine
- port conflicts with local services
- browser must be on machine initiating ssh
future improvements
the community has requested:
- copy-paste authentication flow
- device code flow (like github cli)
- environment variable for callback url
- configurable callback ports