fix(gateway): disconnect WS sessions authed against a rotated shared secret (#2722)#2731
Merged
alexey-pelykh merged 1 commit intoJun 18, 2026
Conversation
…secret (#2722) Rotating the shared gateway auth secret via config.set/patch/apply left WebSocket sessions that authenticated with the OLD secret connected until the next restart — credential revocation was ineffective for live connections. Wire the shared-core disconnect driver together with its config-write caller (the unit-level caller is inert without the integration path in server.impl.ts, so they must land together): - server.impl.ts: gatewayRequestContext.disconnectClientsUsingSharedGatewayAuth closes every usesSharedGatewayAuth socket with code 4001 "gateway auth changed". - server-methods/types.ts: declare the optional context method. - server-methods/config.ts: detect an effective shared-auth change (active mode's secret only, via the existing resolveEffectiveSharedGatewayAuth) and queue the disconnect — config.set when reload is off (no watcher restart would drop them), config.patch/config.apply unconditionally (revoke immediately rather than wait out the delayed SIGUSR1 restart). - Un-skip the two covering tests (server.shared-token-session-rotation, config.shared-auth) that are the acceptance gate. Device-token and other non-shared sessions are preserved by the usesSharedGatewayAuth guard. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
alexey-pelykh
deleted the
fix/gateway-rotate-shared-secret-disconnect-ws-2722
branch
June 18, 2026 09:27
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.
Summary
Closes #2722. Rotating the shared gateway auth secret via
config.set/config.patch/config.applyleft WebSocket sessions that authenticated against the old secret connected until the next gateway restart — credential revocation was ineffective for live connections (HIGH).This wires the shared-core disconnect driver together with its config-write caller. They must land together: the unit-level caller is inert without the integration path in
server.impl.ts, which is why the two covering tests wereit.skip-ped as one deferred hardening item.Changes
src/gateway/server.impl.ts—gatewayRequestContext.disconnectClientsUsingSharedGatewayAuth()iterates the connected-client set and closes everyusesSharedGatewayAuthsocket with code4001 "gateway auth changed".src/gateway/server-methods/types.ts— declares the optionaldisconnectClientsUsingSharedGatewayAuth?: () => voidonGatewayRequestContext.src/gateway/server-methods/config.ts— detects an effective shared-auth change (active mode's secret only, via the existingresolveEffectiveSharedGatewayAuth) and queues the disconnect after the success response:config.set— when reload is off (no watcher restart would otherwise drop the stale sessions).config.patch/config.apply— unconditionally on an effective rotation (revoke immediately rather than wait out the delayed SIGUSR1 restart).server.shared-token-session-rotation.test.tsandserver-methods/config.shared-auth.test.ts.The connection-side flag (
GatewayWsClient.usesSharedGatewayAuth, set inmessage-handler.ts) was already present in the fork; only the driver, the context-type field, and the caller wiring were missing.Device-token and other non-shared sessions are preserved by the
usesSharedGatewayAuthguard.Acceptance criteria
reload.mode: "off"— no restart path — and asserts the old-token socket closes4001.)Testing
pnpm test:gateway(full suite): 141 files, 1559 passed, 7 skipped (the 7 are unrelated pre-existing deferrals). No regressions.pnpm check: format ✓, typecheck ✓, lint 0/0 ✓, css-class-drift 0 orphans ✓.Follow-up (out of scope — not bundled)
The security review noted an orphaned dead-code module
src/gateway/server-shared-auth-generation.ts(plus the unusedsharedGatewaySessionGeneration?field inws-types.ts) — an upstream-sync artifact implementing a parallel generation-based revocation mechanism with zero live callers. It is inert (not a credential hole) but worth a separategut/cleanup pass to avoid future confusion. Intentionally not addressed here to keep this fix scoped.🤖 Generated with Claude Code