Close live Action Cable connections on sign out - #268
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
Pull request overview
Closes live Action Cable connections when a user signs out.
Changes:
- Exposes the remote-disconnect method.
- Disconnects connections after session destruction.
- Adds controller regression coverage.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
app/controllers/concerns/authentication.rb |
Disconnects connections during sign-out. |
app/models/user.rb |
Makes connection closure publicly callable. |
test/controllers/sessions_controller_test.rb |
Tests sign-out disconnection. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 29bd2e0b8a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
29bd2e0 to
1d62e7e
Compare
Action Cable authorizes a Connection once at the WebSocket handshake and never re-checks it. Destroying the session record refuses future handshakes and HTTP requests bearing the cookie, but a socket opened before sign out keeps its handshake-time current_user and keeps authorizing new subscriptions and delivering frames as the signed-out user. Reset the user's remote connections when the session is terminated. Clients tear down and reconnect: the signed-out device carries a destroyed session and cleared cookie and is rejected at the fresh handshake, while the user's other devices with still-valid sessions reconnect and stay live. This reuses the existing reset_remote_connections primitive already used on membership removal, for the same reason. Run the disconnect last and best-effort, after the session record and cookie are already gone, so sign out completes even when the realtime service is unreachable.
1d62e7e to
9126a7e
Compare
Property
Action Cable authorizes a
Connectiononce, at the WebSocket handshake, and never re-checks it. When a user signs out we destroy theirSessionrecord, which refuses future handshakes and any HTTP request carrying the revoked cookie. But a socket that was already open before sign out retains its handshake-timecurrent_userand keeps authorizing new subscriptions and delivering frames — rooms, messages, presence, typing — as the signed-out user.Signing out should also drop the user's live realtime connections.
Fix
terminate_current_sessionnow resets the current user's remote connections as part of sign out:reset_remote_connectionsdisconnects withreconnect: true, so every socket for the user tears down and re-handshakes:find_verified_user— the stale socket dies;This reuses the same primitive already used on membership removal (
app/models/user.rb), for the same reason: connections must re-evaluate authorization.The disconnect runs last and best-effort — after the session record and cookie are already gone — so a realtime-service outage can neither abort sign out nor leave the authentication cookie in place.
Tests
Two regression tests in
SessionsControllerTest:reconnect: true);Both verified RED→GREEN.
Scope and residual
RemoteConnections#whererequires every declared connection identifier, so scoping by session would break the user-wide disconnect that the deactivate, ban, and membership-removal paths depend on.reconnect: truekeeps the user's other valid sessions live, so the user-wide reset carries only a brief, self-healing reconnect.