Аутентифікація з ActionCable та Devise на Rails

3 хв. читання

ActionCable - новий фреймворк для зв'язку в реальному часу, реалізованому з допомогою протоколу websocket, а також він буде частиною Rails 5. Я не збираюся сильно заглиблюватись у все, ви можете більш детально ознайомитись зі всім, що вас буде цікавити на сторінці проекту за цим посиланням.

Websocket сервер запускається в іншому процесі, ніж головний сервер Rails, тому нам потрібно здійснити автентифікацію користувачів також і тут. В прикладі Девід (розробник ActionCable та контрибютор Rails) використав звичайний метод автентифікації, базований на куках, в самому застосунку, а також ревалідація їх в websocket з'єднанні. Це прекрасно демонструє задумку, але багато із користувачів Rails використовують Devise, тому я хочу з вами поділитись тим, як вирішив цю проблему я.

Websocket сервер не має сесії (session), але може зчитувати ті ж куки з самого застосунку, тому я подумав, що зможу просто взяти куки із user id, та перевірити їй же через socket з'єднання. Щоб це зробити, я використав Warden хук:

# 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
....

І ще на кінець нам потрібно зробити куку недійсною, якщо користувач закінчив сесію - вийшов із сайту. Це можна зробити з допомогою іншого Warden хука:

# 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
...

І це все. Тепер я можу ділитись Devise автентифікацією із моїм сервером websocket. Якщо вам цікаво це побачити на прикладі, то можете подивитись на мій форк actioncable-example.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.7K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація