5

I have two Devise models, User and Business; I would like both to be able to sign in using a single sign in form. I am using backbone js and I have a customized view so the view is not a concern. An ajax request is used for the login, it works for users as expected but not for businesses.

I have searched google and come across a few solutions that mention using STI to solve this, but the project is very much done and I can't make such a change now. I was thinking along the lines of overriding the Devise sessions controller and:

  1. Check if the given email address is a User, then authenticate the user using Warden.
  2. If no user with that email is found then authenticate with the Business model using Warden.

I can't figure out how to change the code to achieve the above, I don't know how warden works and which params I need to tweak to achieve the above, what functions need to be called. Can anyone point me in the right direction or provide an example of how should I move forward with this.

Thanks.

ian
  • 12,003
  • 9
  • 51
  • 107
Abid
  • 7,149
  • 9
  • 44
  • 51

3 Answers3

7

Don't worry, you're not stuck.

I think your best option is to override Devise's session controller and alter the 'new' and 'create' methods for a session.

So in your own "sessions_controller.rb", you will have something like this:

class SessionsController < Devise::SessionsController
  # GET /resource/sign_in
  def new
    resource = build_resource(nil, :unsafe => true)
    clean_up_passwords(resource)
    respond_with(resource, serialize_options(resource))
  end

  # POST /resource/sign_in
  def create
    resource = warden.authenticate!(auth_options)
    set_flash_message(:notice, :signed_in) if is_navigational_format?
    sign_in(resource_name, resource)
    respond_with resource, :location => after_sign_in_path_for(resource)
  end
end

And in your routes, you would use something like (depending on what you named your user models):

devise_for :users, :controllers => {
    :sessions => 'sessions'
}


devise_for :businesses, :controllers => {
    :sessions => 'sessions'
}

The above session controller is not customized at all. I don't know your code, so I can't really help there. But the basic steps are:

  1. Inspect the auth_options and resource_name variables to understand how they are storing data.
  2. Add your conditional logic to alter those variables if the resource isn't found in the Users table.

Devise is great, and Roles are great, but sometimes having one User model, or even using STI, don't make sense. I'm writing a longer post on this exact issue soon, since I dealt with it in a recent project.

Tzach Zohar
  • 37,442
  • 3
  • 79
  • 85
Bryce
  • 2,802
  • 1
  • 21
  • 46
  • The devise actions have [changed a little](https://github.com/plataformatec/devise/blob/master/app/controllers/devise/sessions_controller.rb). Ensure you copy the actions from the version you are using, then change `resource_name` to match what user type you want logged in (i.e. `:business`). – d_rail Sep 05 '13 at 02:25
  • Hey mate did you ever do that blog post? – hamstar Jan 09 '14 at 00:00
  • @hamstar - No, I didn't. I actually got fed up with Devise the more I got into it, and rolled my own solution. Is there an issue that you're having? I'm happy to answer any questions (assuming I know the answer). – Bryce Jan 09 '14 at 02:41
  • @Bryce I ended up just overriding devises stuff for my solution but would be interested in your own solution if you made it public. Cheers. – hamstar Jan 09 '14 at 04:36
  • @hamstar - Basically, I ended up creating a model called UserAccount, which holds all of the authentication info for any authenticatable user. Then my different types of users (User, Business, etc.) each have a child UserAccount model. Then all sign-ins, etc. just run through controllers that work with UserAccount models, so any user type can use them. – Bryce Jan 09 '14 at 18:22
5

I recently came up with this total hack to make this work. My advice is to strongly consider a single user model with a role, but this works -- at least with the current version of Devise.

https://gist.github.com/jeremyw/5319386

If you monkeypatch or otherwise override default behavior like this, make sure you have a good test suite in case you ever want to try to upgrade Devise.

  • It would be better if you included the relevant source codes and provide the link only as further reference. – Czar Pino Apr 06 '13 at 23:56
4

Consider using only the User model and use CanCan and Rolify to address the unique needs of each type of user. It might mean starting over with some of your code, but it may be easier to maintain in the long run.

Don't just take my word for it. Here's a related question with good responses.

Community
  • 1
  • 1
ob1
  • 1,792
  • 15
  • 20
  • I agree. Go the "roles" way now, before too much time passes and the project needs a third model down the line. – Zabba Sep 24 '12 at 01:44