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
Rails Way to Detect Mobile Device
Collecting Hashes into Openstruct Creates "Table" Entry
API Errors Customization for Rails 3 Like Github API V3
Rails 4 Strong Parameters Failing When Creating Instances in Rails Console
How to Rescue Timeout Issues (Ruby, Rails)
What's the Cleanest Way to Override Activerecord's Find for Both Models and Collections
Ruby Sandboxing VS. Integrating a Scripting Language
Limiting Characters/Words in View - Ruby on Rails
How to Test If All Items in an Array Are Identical
What Are Tainted Objects, and When Should We Untaint Them
"Certificate Verify Failed" Error When Installing Ruby Gems on Windows
Multiple Level Nested Layout in Rails 3
Require Lib in Rspec with Ruby 1.9.2 Brings "No Such File to Load"