ActionCable Devise Authentication
ActionCable is a new framework for real-time communication over
websockets and it will be part of Rails 5. I am not going to get into
too much detail about it, you can read the very detailed readme of the
project on this link: ActionCable.
The websockets server is running in a separate process from the main Rails application which means you need to authenticate your users there too. In the example app, David used a simple cookie based authentication in the app itself and re-validated the cookie at the websocket connection. This is good for demonstration, but many of the Rails based apps are using Devise for authentication so I want to share, how I solved the authentication with Devise.
The websocket server doesn't have a session, but it can read the same cookies as the main app, so I figured, I will just set a cookie with the user id and verify that at the socket connection. To do this, I used a Warden hook:
This is nice and simple, but I needed some sort of a timeout to expire the session, so I set an expiry time too in the cookies:
One thing left, is to invalidate the cookie on sign out, which can be done in another Warden hook:
That's it, now I can share the Devise authentication with my websocket server.
If you want to see this in an example, you can check my fork of the actioncable-example.
The websockets server is running in a separate process from the main Rails application which means you need to authenticate your users there too. In the example app, David used a simple cookie based authentication in the app itself and re-validated the cookie at the websocket connection. This is good for demonstration, but many of the Rails based apps are using Devise for authentication so I want to share, how I solved the authentication with Devise.
The websocket server doesn't have a session, but it can read the same cookies as the main app, so I figured, I will just set a cookie with the user id and verify that at the socket connection. To do this, I used a Warden hook:
# app/config/initializers/warden_hooks.rb
Warden::Manager.after_set_user do |user,auth,opts|
scope = opts[:scope]
auth.cookies.signed["#{scope}.id"] = user.id
end
# app/channels/application_cable/connection.rb
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.name
end
protected
def find_verified_user
if verified_user = User.find_by(id: cookies.signed['user.id'])
verified_user
else
reject_unauthorized_connection
end
end
end
end
# app/config/initializers/warden_hooks.rb
Warden::Manager.after_set_user do |user,auth,opts|
scope = opts[:scope]
auth.cookies.signed["#{scope}.id"] = user.id
auth.cookies.signed["#{scope}.expires_at"] = 30.minutes.from_now
end
# app/channels/application_cable/connection.rb
...
protected
def find_verified_user
verified_user = User.find_by(id: cookies.signed['user.id'])
if verified_user && cookies.signed['user.expires_at'] > Time.now
verified_user
else
reject_unauthorized_connection
end
end
....
# app/config/initializers/warden_hooks.rb
...
Warden::Manager.before_logout do |user, auth, opts|
scope = opts[:scope]
auth.cookies.signed["#{scope}.id"] = nil
auth.cookies.signed["#{scope}.expires_at"] = nil
end
...
Comments
Post a Comment