1

Today I came across a rather weird phenomenon. When developing a Rails app in which every user has his own subdomain, and trying to use Devise to do it, I encountered that also subdomains not registered whatsoever would route to the root page. So, for example, even if there was no (explicit) subdomain, it would route me to the main application page. Maybe this has to do with my particular setup? I also tried with a new Rails project, but I got the same result. Anyone able to clarify this for me? Railscasts didn't do the trick.

Also, right now I'm using WEBrick, even though that's not what I'll be using in production, and I'm using the domain lvh.me to access the subdomains.

Thanks for the help.

EDIT: Here's my routes.rb file:

Rails.application.routes.draw do
    devise_for :users
    root 'static_page#index'
end

I followed this guide from the Devise wiki: https://github.com/plataformatec/devise/wiki/How-to:-Scope-login-to-subdomain

So what I did was I removed the uniqueness of the email through a migration, and next I added :subdomain to request_keys as follows:

devise :database_authenticatable, :recoverable, :rememberable, :trackable, :timeoutable, request_keys: [:subdomain]

Then I went on to override the "find_for_authentication" function, as per the following:

def self.find_for_authentication(conditions={})
    conditions[:account_id] = Account.find_by_subdomain(conditions.delete(:subdomain)).id
    super(conditions)
end

And that just about sums it up.

EDIT: I've been messing around, and I've found the problem. The 'root' directive refers all subdomains to my domain. So if I remove the root from my routes.rb, every subdomain leads to a RouteError. I remember about needing a root somewhere in my app for Devise. So I'm not sure if this is Devise's behaviour or root's.

Michiel Boekhoff
  • 221
  • 3
  • 13
  • What do you mean "Railscasts didn't do the trick"? How much more can it be spelled out for you? – maček Jul 10 '14 at 04:26
  • Excuse me, but Railscasts didn't exactly spell out the scenario where EVERY subdomain gets routed to the main page, even though there's no routes that make it do that! – Michiel Boekhoff Jul 10 '14 at 04:28
  • 1
    If you want help fixing your code, you need to show us the code you're working with, and then explain the problem. – sevenseacat Jul 10 '14 at 04:38
  • Hi @Alex, I'm not familiar with that rasilcast episode... can you copy in any other code you added to get that domain-thing working? (note: some railscast are getting pretty old and might rely on an older version of rails - plus if you include all the code here, we might spot something obvious that was missed) – Taryn East Jul 10 '14 at 05:00
  • Basically, I followed the tutorial, then I went to see some Railscasts to see if there wasn't something I might've missed. – Michiel Boekhoff Jul 10 '14 at 05:12

2 Answers2

4

There are several issues you may have

The first is you need to appreciate the way in which Devise redirects your user after login, and secondly how subdomains are routed in Rails.

--

Devise

By default, Devise routes to current_user_path (which typically means users#show) or something in your routes:

def after_sign_in_path_for(resource)
  current_user_path
end

This means when you accept the login from the user, they will be taken to their own path. Depending on your routes, this will generally mean the main site (no subdomain) user's path (domain.com/users/56) or something.

Without any specifics on this from your question, I can only speculate on this.

--

Subdomains

Having just worked on some subdomain-enabled apps, there is something you should consider about routing to subdomains.

Once your user has signed in, they need to be able to be routed to a specific subdomain. The way to do this is to use a constraint in your routes:

#config/routes.rb
constraints { subdomain: 'admin' } do
    resources :photos
end

We've found you cannot do this using normal routing paths - you have to use the url (not path helper). For example:

photos_path(subdomain: current_user.name) #-> does not work (path is relative)
photos_url(subdomain: current_user.name) #-> will route to http://name.lvh.me:3000

What you have to remember is if you're looking to redirect / route traffic to different subdomains, you will need to reference the url form of the helper, not the path reference.

So if you take the after_sign_in_path_for as shown above, you'll want to do something like this:

def after_sign_in_path_for(resource)
   root_url(subdomain: resource.name)
end

--

Sessions

Finally, you want to ensure your Devise session cookies will remain initialized after you've set them. We've found subdomains aren't handled by default, so you have to ensure they are catered for:

Share session (cookies) between subdomains in Rails?

#config/initializers/session_store.rb
YOUR_APP_NAME::Application.config.session_store :cookie_store, key: '_app_name_session', domain: :all, tld_length: 2 
Community
  • 1
  • 1
Richard Peck
  • 76,116
  • 9
  • 93
  • 147
  • Well, my subdomain routing works -- just not the way I expected it. I'm writing somewhat of a company portal: each company has his own subdomain and they can login directly like so: foo.site.com and they'll be referred to a login window. Right now, I set up a has_many association between Account (the company, which has the subdomain property) and the User. The user is the Devise resource being managed. But even though, there exists **no** company with subdomain 'foo', but even though I go to foo.site.com, it routes me just fine to the login page. – Michiel Boekhoff Jul 10 '14 at 13:19
1

I solved it manually. I found out the 'root' directive in the routes.rb accepts requests from every subdomain. In the end, I implemented a custom constraint and added this to the constraint for the devise_for in the routes.rb file. This adequately solved my problem. Thank you all for your help!

Update:

In response to the comment asking for my code: we had a system set up whereby each account (business account) would have their own subdomain, and each business account would have many users. My subdomain constraint looks like

lib/subdomain.rb

class Subdomain
  def self.matches?(request)
    request.subdomain.present? && Account.exists?(subdomain: request.subdomain)
  end
end

Then in my config/routes.rb I used that constrain to route the requests:

config/routes.rb

require 'Subdomain'

Rails.application.routes.draw do
  # Some routes...
  constraints(Subdomain) do
    devise_for :users, controllers: {:sessions => 'session'}
    root 'dashboard#index'
    # More subdomain constrained routes...
  end
end

In my original question I'd posted I'd already shown the rest of my setup. I hope this helps someone.

Michiel Boekhoff
  • 221
  • 3
  • 13