switch_user
switch_user copied to clipboard
redirect_path across subdomains (multi-tenancy)
My Admins log in on one subdomain (admin), but the Users that they can impersonate use a subdomain that depends on their account (multi-tenant setup).
I haven't been able to coax switch_user to be able to redirect to the proper subdomain. In fact, my changes there seem to have no effect at all.
In my initializer, I have:
config.redirect_path = lambda { |request, params| authenticated_root_url(subdomain: current_user.subdomain) }
I realize that current_user probably isn't in scope, but I don't even this this code is being hit...
My logs (on attempting to switch user) look like the following:
[admin] Started GET "/switch_user?scope_identifier=user_5" for 127.0.0.1 at 2013-06-06 16:41:06 +0200
[admin] Processing by SwitchUserController#set_current_user as HTML
[admin] Parameters: {"scope_identifier"=>"user_5"}
[admin] Completed 401 Unauthorized in 4ms
[admin]
[admin]
[admin] Started GET "/users/sign_in" for 127.0.0.1 at 2013-06-06 16:41:06 +0200
[admin]
ActionController::RoutingError (No route matches [GET] "/users/sign_in"):
It makes sense that the route is not found (it's based on a route constraint), but in general, I'm unsure of how to get this to work.
Any tips?
I'm a bit confused by the "Completed 401 Unauthorized" at GET /switch_user
BTW the 401 is puzzling to me too. switch_user does a 403 if you don't have access, but not a 401. Could the 401 be from something helping you with your multi-tenancy ? What happens if you don't try to redirect across subdomains ?
The strange part is that it doesn't seem to matter what I put into the redirect_path lambda... I always get the same result!
That's a good point you bring up about the multi-tenancy. I'm using a gem called devise_basecamper to achieve multi-tenancy by subdomain; will have to look into how it functions a bit more.
It turned out to have nothing to do with devise_basecamper...
In my ApplicationController, I have "before_filter :authenticate_user!", so I don't have to repeat it in all of my controllers (basically my whole app is behind authentication).
Since SwitchUserController also inherits from ApplicationController, that before filter causes a 401 unauthorized when we try to call SwitchUserController#set_current_user.
In my controllers where I don't need auth, I would just add "skip_before_filter :authenticate_user!", so something similar to this would need to be done in SwitchUserController. Any idea on how to structure this, without coupling too closely to devise?
I'll also need to make a fancier switch_user_helper that will redirect to the proper subdomain, so I hope you will accept a pull request once I have something working.
Of course, just getting rid of that authentication filter will only work in development...
I'll need to think of a way to switch to the User (using his own subdomain "switch_user" URL) while verifying that the Admin that is trying to switch the User is signed in on the "admin" subdomain. Tricky... Any thoughts?
You raise an interesting point for me, which is should SwitchUserController inherit from ApplicationController or ActionController::Base ? For now, I think the way around this would be to create another controller class (AuthController) that sits between you controllers and application controller. It's in that controller that you'd put the authorization filter. eg
class MyController < AuthController
end
class AuthController < ApplicationController
before_filter :authorize_user!
end
class ApplicationController < ActionController::Base
# no before filter here
end
Is the idea that in production, someone could log in as an admin and then become another user ? I don't feel clear on the use case.
Yeah, that workaround makes sense. I arrived at the same solution myself, in order to not have to modify the gem.
My use case is the following:
- 2 user classes (Admin and User)
- Users login through account-specific login pages (each user is tied to an Account object, and each of those has a subdomain associated with it)
- Admins log in at the "admin" subdomain, and every Admin should be able to impersonate any User. It must work this way in production.
I'm starting to think that what I want to do is more complicated that I thought.
Cookies (by default) aren't shared between subdomains, so I can't think of a secure way to simultaneously authenticate the Admin (on the "admin" subdomain), while logging him in as a User (on the user's account's subdomain).
I can allow the cookie to be shared, but I'm not sure if that would be a good move for security.
Thoughts?