开发者

Devise: Have multiple controllers handle user sessions

开发者 https://www.devze.com 2023-03-08 11:34 出处:网络
I am running devise 1.3.4 with rails 3.0.7. I have two ways users may sign in: using the web app, and using a mobile web app (via a JSON API call). The first way is handled perfectly by the default de

I am running devise 1.3.4 with rails 3.0.7. I have two ways users may sign in: using the web app, and using a mobile web app (via a JSON API call). The first way is handled perfectly by the default devise sessions controller. The API-call method of authentication needs to be in a controller that extends my Api::BaseController. So, I wrote this second controller like this:

class Api::UserSessionsController < Api::BaseController
  …
  def create
    user = warden.authenticate(:scope => :user)
    if user
      sign_in(:user, user)
    else
      # Do some error handling
    end
  end
end

Attempts to login via this method fail due to the valid_controller? method in Devise::Strategies::Authenticatable. Because I have left the default controller (devise/sessions) as the mapped controller for users, it does not allow authentications from my custom controller.

I would like t开发者_运维技巧o roll my custom functionality into my own subclass of Devise::SessionsController, but I need the API sessions controller to extend the API::BaseController, so I can't extend Devise::SessionsController as well. I don't want to place the working, default-behavior web-app authentication methods in the API controller, especially because this would require copying them from the devise controller.

Any suggestions? Is there some config I'm missing that allows multiple controllers to handle sessions? the valid_controller? method does an == comparison, not .include?, so I don't see how that would work.

UPDATE

This is an awful temporary workaround. I don't like it, so I'm not posting it as an answer, but I thought it might offer food-for-thought to all you answerer-types:

In the top of my create method, I could override what Devise expects to be the sessions controller.

Devise.mappings[:user].controllers[:sessions] = params[:controller]

This is working around Devise's intended functionality (requiring a single, specific controller to do session creation) so I don't want to keep it. I wonder if this constraint is a security measure or just a convention -- if it is for security, this is presumably quite bad.


I can only suggest another workaround (maybe less awful?) In an initializer you can overwrite #valid_controller?, like this:

require 'devise/strategies/authenticatable'
require 'devise/strategies/database_authenticatable'

class Devise::Strategies::DatabaseAuthenticatable
  def valid_controller?
    # your logic here
    true
  end
end

I'd be interested in knowing the reason of this constraint too


I'm using Devise 2.2.7 with Rails 3.2.13. Both of the above methods did not work for me: the valid_vontroller? method in Devise::Strategies::DatabaseAuthenticatable does not exist anymore. The Devise.mappings[:user].controllers[:sessions] trick did not work for me either.

After digging through this thread I found valid_params_request? which is responsible for ensuring that the request should be sent through the authentication system. There is a helper method, allows_params_authentication!, which is what enables the Devise::SessionsController to process authentication requests.

You can authenticate a user from any controller by:

def signin
  allow_params_authentication!
  authenticate_user!
end

If you want to redirect to a custom page when authentication fails:

resource = warden.authenticate!({
  :scope => :user,
  :recall => "#{controller_path}#login"
})
sign_in(:user, resource)

I came across the need for handling authentication outside a subclass of Devise::SessionsController by developing an engine that works alongside of Spree Commerce, which already implements devise authentication.

0

精彩评论

暂无评论...
验证码 换一张
取 消