Skip to content

Close live Action Cable connections on sign out - #268

Merged
jeremy merged 1 commit into
mainfrom
signout-closes-live-connections
Sep 1, 2026
Merged

Close live Action Cable connections on sign out#268
jeremy merged 1 commit into
mainfrom
signout-closes-live-connections

Conversation

@jeremy

@jeremy jeremy commented Aug 31, 2026

Copy link
Copy Markdown
Member

Property

Action Cable authorizes a Connection once, at the WebSocket handshake, and never re-checks it. When a user signs out we destroy their Session record, 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-time current_user and 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_session now resets the current user's remote connections as part of sign out:

def terminate_current_session
  Current.session&.destroy!
  reset_session
  remove_authentication_cookie
  disconnect_remote_connections
end

reset_remote_connections disconnects with reconnect: true, so every socket for the user tears down and re-handshakes:

  • the signed-out device carries a destroyed session and a cleared cookie, so its reconnect is rejected at find_verified_user — the stale socket dies;
  • the user's other devices with still-valid sessions reconnect and stay live, so signing out on one device doesn't silently freeze another.

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:

  • sign out fires the remote-connection reset for the signed-out user (reconnect: true);
  • sign out still redirects, clears the cookie, and destroys the session when the realtime service raises.

Both verified RED→GREEN.

Scope and residual

  • Message posting is HTTP-gated and already refused once the session is destroyed, so this specifically closes the realtime read/presence/typing surface that a lingering socket kept open.
  • Disconnects are user-wide rather than session-scoped: Action Cable's RemoteConnections#where requires 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: true keeps the user's other valid sessions live, so the user-wide reset carries only a brief, self-healing reconnect.
  • This shares Action Cable's inherent limitation for any remote-disconnect-based revocation (already relied on by the deactivate and ban paths): a handshake completing in the narrow window between session destruction and the disconnect broadcast can miss that one-shot signal. Because the session record is already gone, such a socket is rejected on its next reconnect rather than persisting indefinitely. Durable per-subscription revalidation would be a larger, separate change.

Copilot AI balanced review requested due to automatic review settings August 31, 2026 23:05
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-31T23:19:29.463331Z 9126a7e New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@jeremy
jeremy marked this pull request as draft August 31, 2026 23:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread app/controllers/concerns/authentication.rb Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread app/models/user.rb Outdated
Comment thread app/controllers/concerns/authentication.rb Outdated
Comment thread app/controllers/concerns/authentication.rb Outdated
@jeremy
jeremy force-pushed the signout-closes-live-connections branch from 29bd2e0 to 1d62e7e Compare August 31, 2026 23:11
@jeremy
jeremy marked this pull request as ready for review August 31, 2026 23:12
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.
@jeremy
jeremy force-pushed the signout-closes-live-connections branch from 1d62e7e to 9126a7e Compare August 31, 2026 23:16
@jeremy
jeremy merged commit 9dd19d0 into main Sep 1, 2026
12 checks passed
@jeremy
jeremy deleted the signout-closes-live-connections branch September 1, 2026 01:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants