[WSL2][Auth] Device-code final token exchange queries 127.0.0.1:53 instead of configured DNS resolver
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
What version of Codex are you using?
Reproduced on:
codex-cli 0.152.1codex-cli 0.153.0
Platform
- Linux x86_64 under WSL2
/etc/resolv.confis configured with the WSL DNS resolver (10.255.255.254in this reproduction)- No DNS service is listening on
127.0.0.1:53
Summary
codex login --device-auth successfully obtains a device code, successfully polls the device-auth endpoint, and the browser approval succeeds. The failure occurs only at the final OAuth token exchange.
A packet-level capture of DNS traffic during the full flow shows that the working polling path and the failing final exchange use different DNS servers:
polling path:
auth.openai.com -> 10.255.255.254:53 -> response received
post-approval final exchange:
auth.openai.com -> 127.0.0.1:53 -> no response
retransmit -> 127.0.0.1:53 -> no response
final token exchange fails
The final error is:
oauth token exchange transport failure is_timeout=false is_connect=true is_request=true
error=error sending request for url (https://auth.openai.com/oauth/token)
No TCP connection to an OpenAI endpoint is attempted after the failing loopback DNS queries.
Packet-level reproduction
- Start a DNS packet capture in WSL2, for example:
sudo tcpdump -ni any -tttt -vv 'port 53'
- Run:
codex login --device-auth
-
Complete the device authorization in the browser.
-
Observe during the polling phase that DNS requests for
auth.openai.comare sent to the configured WSL resolver and receive normal responses. -
Immediately after successful browser approval, observe a fresh DNS lookup for
auth.openai.combeing sent to127.0.0.1:53instead of the configured resolver. In this environment nothing listens on loopback port 53, so the lookup receives no response and is retransmitted. -
The final
/oauth/tokenexchange then fails with the transport error above.
Additional checks
The following were independently checked in the same environment:
auth.openai.comis reachable from the same WSL2 environment.- Fresh
curlprocesses can resolve the hostname, establish TCP/TLS, and POST to the same/oauth/tokenendpoint successfully. - The configured WSL resolver answers promptly during the same Codex login run.
- No DNS listener is present on
127.0.0.1:53. - Disabling IPv6 does not change the failure.
- The behavior reproduces on both 0.152.1 and 0.153.0.
Relevant Codex source path
The final exchange uses a newly constructed auth client via create_raw_auth_client(...), which calls:
auth_route_config
.http_client_factory()
.build_client_without_request_logging(endpoint, ClientRouteClass::Auth)
This is consistent with the packet trace showing a fresh resolution attempt at the final exchange boundary.
I am not claiming the exact internal reason that this fresh client selects 127.0.0.1:53; that lower-level mechanism is still unresolved. The packet-level behavior itself is directly observed.
Expected behavior
The fresh auth-exchange client should use a valid resolver for the active runtime, consistent with the resolver used by the successful polling path.
Actual behavior
After browser approval, the fresh auth-exchange client sends DNS queries for auth.openai.com to 127.0.0.1:53, where no DNS server is running, and the final OAuth token exchange fails before TCP connection establishment.
Related issues
Similar top-level token-exchange symptoms have been reported in #40839 and #41434, but those reports describe different environments and do not identify this WSL2 packet-level resolver switch to loopback during the final exchange.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at create_raw_auth_client(...) and the auth_route_config().http_client_factory().build_client_without_request_logging(...) path, then compare it with the client used by the successful polling flow. Reproduce under WSL2 with the provided tcpdump command and verify which resolver each client uses. Done means the final token exchange resolves auth.openai.com through the active configured resolver instead of 127.0.0.1:53.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- authentication, networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100