Skip to content

access login self-deadlocks on its own token lock file when edge token verification fails (e.g. binding cookie enabled) #1692

Description

@erwinkn

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. loginverifyTokenAtEdgeisTokenValidcarrier.BuildAccessRequesttoken.FetchTokenWithRedirect (fetch Refactor to separate command from library functions #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 Refactor to separate command from library functions #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 Cannot register tunnel #2 in the same processacquireLockFile 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-<aud>-token.lock stale_pid=95277
2026-07-08T13:18:01Z DBG lock file acquired path=/Users/x/.cloudflared/app.example.com-<aud>-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-<aud>-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://<app> 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions