fix(auth): refresh the token before a subscription (re)connect - #1103
Open
codyde wants to merge 1 commit into
Open
fix(auth): refresh the token before a subscription (re)connect#1103codyde wants to merge 1 commit into
codyde wants to merge 1 commit into
Conversation
`subscribe_graphql` read the bearer straight off disk and never called `ensure_valid_token`. A WebSocket authenticates once, at the upgrade, so a connection opened with a good token keeps working past its expiry and the staleness only shows up on reconnect — where `stream_http_logs_inner` retries a dropped stream up to twelve times, re-presenting the same dead bearer and failing the handshake identically every time. Net effect today: a `railway logs -f` that outlives its access token dies at the first network blip, with twelve identical failures and no path back. #1102 fixed this staleness for the HTTP path and did not touch the WebSocket path. Doing it now because an always-on cloud-agent subscription — a session that is *expected* to outlive many token lifetimes — makes it load-bearing. Credential resolution moves into `connect_auth_header`, which refreshes first and then picks the header, so it can be tested without standing up a WebSocket server. `ensure_valid_token_at` is `ensure_valid_token` against an explicit token endpoint, which is what lets the test point at a scripted one. A refresh failure stays non-fatal: it only fires once local expiry has passed, the stored token may still be good, and the handshake reports a genuinely dead credential better than a speculative refresh does. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #1102, which fixed bearer staleness on the HTTP path and left the WebSocket path untouched.
Problem
subscribe_graphqlreads the bearer straight off disk and never callsensure_valid_token.A WebSocket authenticates once, at the upgrade. So a connection opened with a good token keeps working past its expiry — the staleness only bites on reconnect, and
stream_http_logs_innerretries a dropped stream up to twelve times. Every attempt re-presents the same expired bearer and fails the handshake identically.Today: a
railway logs -fthat outlives its access token dies at the first network blip, twelve identical failures deep, with no path back.Doing this now rather than later because an always-on cloud-agent subscription — a socket expected to outlive many token lifetimes — would make it load-bearing rather than an edge case.
Fix
subscription.rsconnect_auth_header, which refreshes before picking the header. Testable without a WebSocket server.client.rsensure_valid_token_at—ensure_valid_tokenagainst an explicit token endpoint. Lets the test point at a scripted one;ensure_valid_tokenis now a thin wrapper that derives the URL.A refresh failure stays non-fatal. It only fires once local expiry has passed, the stored token may still be good, and the handshake reports a genuinely dead credential better than a speculative refresh does.
RAILWAY_TOKENstill short-circuits toproject-access-tokenuntouched.Testing
1124 tests pass, 3 consecutive clean runs, 0 clippy errors.
Two new tests in
auth_sim.rs:Bearer stale-accessif the refresh line is removed — verified)Not covered: the live WebSocket handshake. This checkout has no linked project, so
logs -fcouldn't be exercised end to end. The credential resolution is tested both ways; the remaining change is mechanical — the same two headers, applied from a resolved(name, value)pair instead of two inline branches.Context
Came out of a feasibility review for putting
railway ca's agent/session state on GraphQL subscriptions. That work is blocked on backboard exposing a cloud-agent subscription at all — this fix is the piece that's independent of it and worth landing on its own.🤖 Generated with Claude Code