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:

  1. run codex login on remote machine
  2. cli opens browser with oauth url
  3. complete authentication on openai/chatgpt website
  4. browser redirects to http://localhost:1455/auth/callback?code=...&scope=openid+profile+email+offline_access&state=...
  5. 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

  1. establish ssh connection with port forwarding:

    ssh -L 1455:localhost:1455 username@remote-host
  2. run codex cli on remote machine:

    codex login
  3. complete browser authentication:

    • browser opens authentication page
    • log in with your openai/chatgpt account
    • authorize the application
  4. callback reaches remote cli:

    • browser redirects to localhost:1455/auth/callback
    • ssh tunnel forwards to remote machine
    • cli receives auth code and completes login

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:

  1. add port forwarding in vscode:

    • command palette: “forward a port”
    • enter port: 1455
    • local address: localhost:1455
  2. 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

  1. use ssh config: define forwarding rules permanently
  2. close tunnels: exit ssh sessions after authentication
  3. verify success: test cli commands before closing tunnel
  4. document setup: add to project readme for team members
  5. 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

references

on this page