ruby erb 63 lines · 4 tabs

ActionCable channel that streams Turbo updates safely

Shared by codesnips Jan 2026
4 tabs
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
      logger.add_tags "ActionCable", current_user.id
    end

    private

    def find_verified_user
      if (user = User.find_by(id: cookies.encrypted[:user_id]))
        user
      else
        reject_unauthorized_connection
      end
    end
  end
end
4 files · ruby, erb Explain with highlit

This snippet shows how a Rails app streams live UI updates to a single authenticated user over ActionCable while keeping the stream authorized, so one user can never subscribe to another user's channel. The pattern combines Turbo Streams (which render partials and wrap them in <turbo-stream> frames) with a hand-written ActionCable channel that verifies ownership before calling stream_for.

In Connection, the WebSocket connection is identified by current_user, resolved from an encrypted, signed cookie rather than a request parameter. Because ActionCable connections are long-lived and separate from the normal request cycle, find_verified_user rejects the connection with reject_unauthorized_connection when no valid session exists. This means authentication happens once at connect time, and every channel on that connection inherits the trusted current_user.

NotificationsChannel is where the real safety lives. The client sends a subscribed request with a notifiable_id, but the channel never trusts that id blindly — it scopes the lookup to current_user.projects, so a forged id for someone else's project raises ActiveRecord::RecordNotFound and the subscription is rejected. Only after the record is confirmed to belong to the connected user does stream_for project open the stream. Using stream_for (rather than a raw stream_from with a string key) lets Rails derive the broadcasting name from the object and the channel class, avoiding manually built, guessable channel names.

The broadcast itself is triggered from Notification model in an after_create_commit callback. broadcast_prepend_later_to enqueues the render on a background job so the transaction commits fast and the ERB partial is rendered off the request thread. It targets the DOM id notifications and renders the Notification partial.

That partial in Notification partial is deliberately plain: a turbo_frame-friendly <li> keyed by dom_id(notification), which Turbo uses to prepend without duplicates. The key trade-off is that authorization must be enforced at subscribe time because the socket bypasses controller filters; forgetting the scoped lookup would leak every project's notifications to every client. This approach fits dashboards, chat, and activity feeds where updates must reach exactly one user in real time.


Related snips

Share this code

Here's the card — post it anywhere.

ActionCable channel that streams Turbo updates safely — share card
Link copied