Have Devise Create a Subdomain on Registration

Have Devise create a subdomain on registration

One subdomain per user is a fairly common use-case in web application development. Here's how you can do it:

First: ensure your Users table has a :name column (I think Devise does this by default - if not you can run rails g migration AddNameToUsers name:string to add this column to your database).

To use this User.name as a subdomain we’ll need to make sure it only contains alphanumeric characters (with an optional underscore). We’ll also limit the name to a maximum of 32 characters. Finally, we don’t want users to choose names such as “www” that will result in URLs such as “http://www.myapp.com”. Here's the validations for app/models/user.rb:

validates_format_of :name, with: /^[a-z0-9_]+$/, 
message: "must be lowercase alphanumerics only"

validates_length_of :name, maximum: 32,
message: "exceeds maximum of 32 characters"

validates_exclusion_of :name, in: ['www', 'mail', 'ftp'],
message: "is not available"

Optionally: modify your db/seeds.rb (so it creates test users when you initialize the database):

user = User.create! :name => 'myname', :email => 'user@example.com', 
:password => 'password', :password_confirmation => 'password'

We’ll display a profile page for a user when anyone enters a URL with a subdomain that matches an existing user app/controllers/profiles_controller.rb:

class ProfilesController < ApplicationController
def show
@user = User.where(:name => request.subdomain).first || not_found
end

def not_found
raise ActionController::RoutingError.new('User Not Found')
end
end

Here's an example file for the view app/views/profiles/show.html.erb:

<h1>Profile</h1>
<h3><%= @user.name %></h3>
<h3><%= @user.email %></h3>

Lastly we need to implement routing for the subdomains. Create a class like this:

class Subdomain
def self.matches?(request)
case request.subdomain
when 'www', '', nil
false
else
true
end
end
end

Make sure this class is autoloaded when the application starts config/application.rb:

config.autoload_paths += %W(#{config.root}/lib)

Ensure your routes.rb file contains the following routes:

devise_for :users
resources :users, :only => :show
constraints(Subdomain) do
match '/' => 'profiles#show'
end

If you used rails generate for your profiles controller - ensure that you remove the get "profiles/show" route.

See this page for information on using URL Helpers in your application (essentially you'll need to use new_user_session_url instead of new_user_session_path and you can specify a subdomain like this:

root_url(:subdomain => @subdomain)

Log a user into their subdomain after registration with Rails and Devise

You could use domain: :all option in your config.session_store and just have a before_action just as suggested by some in the comments.

So you'll still have the code in config/initializers/session_store.rb or in config/application.rb:

config.session_store :cookie_store, :key => '_domain_session', :domain => :all

Then in your application_controller add the following code:

#app/controllers/application_controller.rb
before_action :check_subdomain

def check_subdomain
unless request.subdomain == current_user.account.subdomain
redirect_to root_path, alert: "You are not authorized to access that subdomain."
end
end

Devise registration and sign_in scoped by subdomain

There is a howto on this in the devise wiki:

https://github.com/plataformatec/devise/wiki/How-To:--Isolate-users-to-log-into-a-single-subdomain

You would also need to handle saving that info when you create a user, which you could do easily in your users controller (or wherever you save user accounts when they sign up).

Devise - Sign in On Subdomain

You just need to share the user session between the subdomain for this, so that one session can be continued with multiple subdomains.

You should modify the session_store.rb file in initializes as,

  DemoApp::Application.config.session_store :cookie_store, key: '_jcrop_app_session', domain: ".maindomain.com"

Adding the domain will work and careful with the last leading "." (period) which is needed for sub domain.

Devise Scope login to subdomain

I found that by changing account to subdomain that I was able to get past this error.

I had missed the part in the documentation that states

"If you are using column name other than subdomain to scope login to
subdomain, you may have to use authentication_keys. For example, if
you have subdomains table and are using subdomain_id on your Devise
model to scope User, you will have to add authentication_keys:
[:email, :subdomain_id] instead of request_keys: [:subdomain_id] on
user.rb. This is because request_keys honors only predefined keys such
as :subdomain."

As soon as I changed all relations from account to subdomain I moved on to the following error

ActiveRecord::StatementInvalid in Devise::SessionsController#create
PG::UndefinedTable: ERROR: relation "users" does not exist LINE 1:
SELECT "users".* FROM "users" WHERE "users"."email" = $1 AN... ^ :
SELECT "users".* FROM "users" WHERE "users"."email" = $1 AND
"users"."subdomain" = $2 ORDER BY "users"."id" ASC LIMIT $3

This is a new error, so I will close this out.

Can't confirm account for subdomain. Using Devise confirmable and Rails 4

I managed to solve this by using the following in confirmation_instructions.html.erb. This works for my setup, since I'm using the Apartment gem:

<%= link_to 'Confirm my account', user_confirmation_url(confirmation_token: @token, subdomain: Apartment::Tenant.current) %>


Related Topics



Leave a reply



Submit