cloudflare / cloudflare/cloudflared
access login self-deadlocks on its own token lock file when edge token verification fails (e.g. binding cookie enabled)
- Dominant language
- Go
- Stars
- 15.6k
- Forks
- 1.4k
- PR merge metrics
- No merged PRs in 30d
Description
**cloudflared version**: 2026.6.1 (built 2026-06-18), macOS (Darwin 25.5.0)
## Summary
`cloudflared access login` deadlocks forever against its own token lock file whenever the token minted during login fails the edge verification step. The most common trigger: the Access application has **"Enable Binding Cookie"** turned on, so a bare JWT (no bound cookie) is always rejected at the edge. Every retry of the command hangs the same way, and the printed "Another cloudflared process (pid N) is already waiting for authentication" names the process's *own* PID.
## Root cause (traced in master)
1. `login` → `verifyTokenAtEdge` → `isTokenValid` → `carrier.BuildAccessRequest` → `token.FetchTokenWithRedirect` (fetch #1). `getToken` acquires the app-token lock via `acquireLockFile` (`token/token.go`). **This lock is never released during the process lifetime** — reclaim only happens when the owning PID is dead/stale.
2. Fetch #1 succeeds (org-token exchange or browser flow). `isTokenValid` then presents the JWT as a header; with the binding cookie enabled the edge responds with a login redirect → token judged invalid.
3. `verifyTokenAtEdge` calls `token.RemoveTokenIfExists` (deleting the just-minted token) and calls `isTokenValid` again → fetch #2 **in the same process** → `acquireLockFile` finds the lock from step 1, reads its own PID, sees it alive, and waits forever — printing the "Another cloudflared process" banner with its own PID and replaying the stale `.url` companion file from a previous run.
Because the lock owner is alive, the staleness reclaim never fires; the command hangs until `lockTimeout`. Users who Ctrl+C and retry see a rotating cast of "stale" PIDs (each dead predecessor) followed by the same self-deadlock, which makes this very confusing to diagnose. The replayed stale auth URL is also actively harmful: users authorize it in the browser and the resulting transfer goes to a poller that no longer exists.
## Debug trace (hostname redacted)
```
2026-07-08T13:18:01Z DBG reclaiming stale lock file path=/Users/x/.cloudflared/app.example.com--token.lock stale_pid=95277
2026-07-08T13:18:01Z DBG lock file acquired path=/Users/x/.cloudflared/app.example.com--token.lock
Another cloudflared process (pid 13091) is already waiting for authentication.
...
2026-07-08T13:18:02Z DBG lock file is held by another process, retrying path=/Users/x/.cloudflared/app.example.com--token.lock
2026-07-08T13:18:04Z DBG lock file is held by another process, retrying path=...
```
Confirmed: the lock file content was `{"pid":13091,"start_time":1783516681035}` — pid 13091 with that start time (13:18:01.035) **is the process that produced this log**, i.e. it is waiting on itself.
## Repro
1. Create a self-hosted Access application with **Enable Binding Cookie** on.
2. `cloudflared access login https://` on a machine with no cached tokens.
3. Complete the browser authorization → CLI hangs forever; all subsequent invocations hang too.
## Workaround for users
Disable the binding cookie on the Access application (required anyway for any header-based/automated access to the app), then clear `~/.cloudflared/*.lock` and retry.
Contributor guide
Research direction
Start at the login flow's verifyTokenAtEdge and isTokenValid calls, then read token/token.go, especially acquireLockFile and RemoveTokenIfExists. Reproduce with an Access application using Binding Cookie enabled; done means a failed edge verification can retry without waiting on its own PID, and stale authentication URLs are not replayed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- authentication, cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 58/100