Devise: Have multiple controllers handle user sessions
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.
精彩评论