OAuth login fails due to Cloudflare challenge race condition — auth login / setup-token broken during elevated CF protection
Summary
Claude Code's OAuth login flow (claude auth login and setup-token) fails when Cloudflare's bot protection is elevated on claude.ai. The /oauth/authorize endpoint returns 403 with a Cloudflare JS challenge, and the browser must solve the Turnstile challenge before the page loads. By the time the challenge is solved, Claude Code's local callback server has timed out or the flow gets stuck.
Ironic workaround: Running the auth flow through Burp Suite with intercept enabled accidentally fixes the issue — the human-controlled request timing gives the browser enough time to solve the CF challenge before proceeding.
Affects: claude auth login, setup-token, any OAuth-based login
Does NOT affect: ANTHROPIC_API_KEY based auth
Version: Claude Code 2.1.72
OS: macOS (Apple Silicon), Chrome 144
Related incident: https://status.claude.com/incidents/jm3b4jjy2jrt
The Problem
During periods of elevated Cloudflare protection on claude.ai, the OAuth authorize endpoint (/oauth/authorize) is served behind a Cloudflare JS challenge (Turnstile). The normal browser flow:
- Claude Code opens browser →
claude.ai/oauth/authorize?... - Cloudflare intercepts → returns 403 + JS challenge (
Cf-Mitigated: challenge) - Browser solves Turnstile → gets
cf_clearancecookie → page loads - User clicks "Authorize" → redirect to
localhost:PORT/callback - Claude Code receives auth code
The failure: Steps 2-3 can take 15-30+ seconds. If Claude Code's callback server or the PKCE flow has a timeout shorter than the Cloudflare challenge solving time, the auth silently fails.
Evidence (from Burp Suite HTTP history)
I captured the full OAuth flow through Burp Suite Community Edition. Here's the sanitized timeline:
Request Timeline
| Time | Method | Status | CF Cookies | Notes |
|------|--------|--------|------------|-------|
| 17:10:45 | GET | 403 | NONE | First attempt — blocked, no CF cookies |
| 17:10:47 | GET | 403 | NONE | Retry — blocked again |
| 17:10:56 | GET | 403 | __cf_bm only | Has bot management cookie but NO clearance — still blocked |
| 17:11:11 | POST | 200 | cf_clearance + __cf_bm + Turnstile tokens | 26 seconds later — CF challenge solved, auth page loads |
| 17:14:48 | GET /callback | — | — | Auth code delivered to Claude Code's local server |
Key observations
- Three consecutive 403s — the response headers confirm
Cf-Mitigated: challenge - 26-second gap between first 403 and successful 200 — that's the Cloudflare Turnstile solving time
- The successful POST contains three Cloudflare Turnstile challenge tokens in the body (5.2KB total)
- The Referer includes
__cf_chl_tk=— Cloudflare's challenge token parameter cf_clearancecookie is required — requests without it get 403
First request (blocked):
GET /oauth/authorize?code=true&client_id=9d1c250a-...&redirect_uri=http://localhost:PORT/callback HTTP/1.1
Host: claude.ai
# No CF cookies
HTTP/2 403 Forbidden
Cf-Mitigated: challenge
Cf-Ray: 9dabd5033a4427c0-PRG
Successful request (after Turnstile solved):
POST /oauth/authorize?... HTTP/2
Host: claude.ai
Cookie: __cf_bm=REDACTED; _cfuvid=REDACTED; cf_clearance=REDACTED
Content-Length: 5251
[CLOUDFLARE_TURNSTILE_CHALLENGE_TOKEN_1]=REDACTED
[CLOUDFLARE_TURNSTILE_CHALLENGE_TOKEN_2]=REDACTED
[CLOUDFLARE_TURNSTILE_CHALLENGE_TOKEN_3]=REDACTED
HTTP/2 200 OK
X-Request-Pathname: /oauth/authorize
Full sanitized Burp Suite HTTP history (42 OAuth-related requests, all tokens/cookies/keys redacted): https://gist.github.com/gyrusdentatus/350c95e768337c33024373137ebef96c
How We Verified This
1. Burp Suite HTTP Capture (Browser Flow)
Proxied Chrome through Burp Suite Community Edition during the auth outage. Captured 508 requests, filtered to 42 OAuth-related ones. The capture clearly shows:
- Three 403 responses with
Cf-Mitigated: challengeheader - 26-second gap while Cloudflare Turnstile challenge was solved by the browser
- Only after
cf_clearancecookie was obtained did/oauth/authorizereturn 200 - With Burp intercept enabled, manually stepping through requests gave enough time for CF challenge resolution — auth succeeded
2. Docker Container Reproduction (Headless)
We installed Claude Code v2.1.72 in a fresh node:22-slim Docker container and attempted all auth methods:
docker run --rm node:22-slim sh -c '
npm install -g @anthropic-ai/claude-code 2>&1 | tail -1
echo "=== auth login ===" && claude auth login 2>&1
echo "=== setup-token ===" && timeout 10 claude setup-token 2>&1
echo "=== auth status ===" && claude auth status 2>&1
'
Results:
claude auth login— requires TTY, crashes withRaw mode is not supported(Ink library)claude setup-token— same crash, also requires browserclaude auth status—{"loggedIn": false, "authMethod": "none"}- No
--no-openflag, no device code flow, no token paste option
This surfaces a secondary issue: there is zero headless/non-browser auth path besides ANTHROPIC_API_KEY.
3. Direct curl Proof from Docker
From inside a Docker container, we hit the OAuth endpoint directly — no browser, no cookies:
$ curl -sS -D - -o /dev/null \
-H "User-Agent: Mozilla/5.0 Chrome/131" \
"https://claude.ai/oauth/authorize?client_id=9d1c250a-e31a-43b0-a0e4-09fa5bedb0f5&response_type=code&redirect_uri=http://localhost:12345/callback&scope=user:inference"
HTTP/2 403
cf-mitigated: challenge
set-cookie: __cf_bm=...; domain=.claude.ai; HttpOnly; Secure
cf-ray: 9dac1431bf144107-PRG
The response body is Cloudflare's "Just a moment..." page (7.9KB of Turnstile JS). Without a browser to execute the JS challenge, auth is permanently blocked.
Why Burp Suite Intercept Fixes It
When Burp's intercept is enabled, the user manually controls when each request is forwarded. This inadvertently:
- Gives the browser unlimited time to solve the CF Turnstile challenge
- Prevents any timeout race between the challenge and Claude Code's callback server
- The human stepping through requests acts as a "proof of humanity" — the exact thing Cloudflare wants
Without Burp (normal flow), the browser has to solve the challenge AND complete the full OAuth flow within Claude Code's timeout window.
Suggested Fixes
- Whitelist
/oauth/authorizefrom aggressive CF challenge — This is an authenticated user action (the user is already at their computer), not a bot scraping endpoint - Increase Claude Code's callback server timeout — Currently too short for CF challenge scenarios. 60-120s would handle it
- Add retry logic — If the browser gets a 403 on the authorize page, Claude Code should keep its callback server alive longer and possibly retry
- Show a user-facing message — "Waiting for browser authentication... If Cloudflare shows a challenge, please complete it" instead of silently failing
- Consider a device code flow fallback — For headless/problematic environments, allow users to paste a code instead of relying on localhost callback
Reproduction
Browser (during elevated CF protection):
- Run
claude auth loginduring elevated CF protection on claude.ai - Observe the browser gets a Cloudflare challenge page instead of the authorize page
- Auth fails silently (or returns an error after timeout)
Docker (100% reproducible, no CF protection needed):
docker run --rm node:22-slim sh -c 'npm i -g @anthropic-ai/claude-code && claude auth login'- Observe:
Raw mode is not supportedcrash — no headless auth path exists
Environment
- Claude Code: 2.1.72
- OS: macOS 15 (Apple Silicon), also reproduced in Docker (node:22-slim)
- Browser: Chrome 144
- Location: Europe (PRG Cloudflare PoP)
- Burp Suite: Community Edition 2025.12.5
This issue has 8 comments on GitHub. Read the full discussion on GitHub ↗