Authentication

JWT authentication

AnyCable JWT is the best (both secure and fast) way to authenticate your real-time connections.

With AnyCable Rails, all you need is to configure the AnyCable application secret (anycable.secret in credentials or ANYCABLE_SECRET env var) and replace the #action_cable_meta_tag with #action_cable_with_jwt_meta_tag:

<%= action_cable_with_jwt_meta_tag(user: current_user, tenant: Current.tenant) %>

# => <meta name="action-cable-url" content="ws://demo.anycable.io/cable?token=eyJhbGciOiJIUzI1NiJ9....EWCEzziOx3sKyMoNzBt20a3QvhEdxJXCXaZsA-f-UzU" />

You MUST pass current user's connection identifiers as keyword arguments to provide identity information.

Connection identifiers are the connection class parameters you define via the .identified_by method and set in the #connect method. For example:

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :user, :tenant

    def connect
      self.current_user = find_verified_user
      self.tenant = find_current_tenant
    end
  end
end

IMPORTANT: When using AnyCable JWT, the Connection#connect method is never called (for clients using JWT tokens). We associate the connection identifiers with the client at the AnyCable server side and make them accessible in subsequent commands. However, if you have some additional logic in your Connection#connect method (e.g., tracking users activity), it won't be preserved.

By default, tokens are valid for 1 hour. You can change this value by specifying the jwt_ttl configuration parameter.

Manually generating tokens

If you're not using HTML, you can generate AnyCable JWT by using the following API:

token = AnyCable::JWT.encode({user: current_user})

# you can also override the global TTL setting via expires_at option
token = AnyCable::JWT.encode({user: current_user}, expires_at: 10.minutes.from_now)

Using AnyCable JWT with Action Cable

You can use AnyCable JWT authentication with Rails Action Cable (especially useful when you're gradually migrating to AnyCable). For that, update your ApplicationCable::Connection class as follows:

 module ApplicationCable
   class Connection < ActionCable::Connection::Base
+    prepend AnyCable::Rails::Ext::JWT
+
     identified_by :user, :tenant

     def connect
+      return identify_from_anycable_jwt! if anycable_jwt_present?
+
       self.current_user = find_verified_user
       self.tenant = find_current_tenant
     end
   end
 end

Tokens expiration

AnyCable server checks a token's TTL, and in case the token is expired, the server disconnects the client with a specific reason: token_expired. You can learn more about how to refresh the token in this post.

Cookies & session

Cookies and Rails sessions are supported by AnyCable Rails. If you run AnyCable server on a different domain from your Rails application, make sure your cookie store is configured to share cookies between domains. For example, to share cookies with subdomains:

config.session_store :cookie_store, key: "_<my-app>_sid", domain: :all

Rack middlewares

If your authentication method relies on non-standard Rack request properties (e.g., request.env["something"]) for authentication, you MUST configure AnyCable Rack middleware stack to include required Rack middlewares.

Devise/Warden

Devise relies on warden Rack middleware to authenticate users.

By default, this middleware is automatically added to the AnyCable middleware stack when Devise is present.

You can edit config/anycable.yml to disable this behavior by changing the use_warden_manager parameter.

# config/anycable.yml
development:
  use_warden_manager: false

And then, you can manually put this code, for example, into an initializer (config/initializers/anycable.rb) or any other configuration file.

AnyCable::Rails::Rack.middleware.use Warden::Manager do |config|
  Devise.warden_config = config
end

Then, you can access the current user via env["warden"].user(scope) in your connection class (where scope is Warden scope, usually, :user).