Rails: Multi-tenancy with Devise and Apartment gem - ruby-on-rails

I'm creating a multi-tenant app using devise and apartment gems. I'm using postgresql database. I've created a few models. 'User' model is in global namespace and it is used for authentication by devise gem. There are some other models (e.g. Project, Setting etc) which are in tenant namespace.
I've followed this tutorial for creating this multi-tenance app: https://gorails.com/episodes/multitenancy-with-apartment?autoplay=1
The multi-tenancy feature is working fine in a sense that if I login to two separate subdomains (e.g. user1.example.com and user2.example.com) from their relevant accounts (e.g. user1#gmail.com and user2#gmail.com) it works fine and I can create unique records for each tenant.
Now, the issue is, I can login to any subdomain using any email and the tenant records would be shown based on the subdomain present in address bar. e.g. I can login with user1#gmail.com at user2.example.com and it will succesfully autheticate and will display records of user2 tenant.
My question is, while logging in how can I check if current user's subdomain matches with the requested subdomain (on address bar), if it matches proceed with authentication and display admin dashboard and if not (logging in from wrong subdomain or from TLD) authenticate the user but redirect him to his relevant subdomain's dashboard. How can I do that?
UPDATE # 1:
I was able to restrict the user login to their specific sub-domain by using minor devise configuration. In devise.rb file I've added :subdomain attribute in the list of authentication keys, so it will also check for correct subdomain value together with email, however I'm not sure how to provide the subdomain value to the login form correctly. I can use a hidden field like this in login form <%= f.hidden_field :subdomain, value: request.subdomain %> but it is hackable as user can change it's value from browser inspector.
UPDATE # 2:
I was able apply a fool proof method to restrict user login to their specific sub-domain. I've followed this method: https://github.com/plataformatec/devise/wiki/How-to:-Scope-login-to-subdomain
Now, my only issue is that user is unable to login from TLD (e.g. example.com), I want it to be possible but after login user must be redirected to their relevant sub-domain with alive session.

Supposing you're saving the subdomain on the User model, you can create a validation in your controller you can use something like:
if user.subdomain == request.subdomain
redirect_to root_url(subdomain: user.subdomain)
else
everything_is_ok
end

If you store your subdomain in your User table and if you check it via request.subdomain then how can someone join the another tenant(company) ? They can be included in more than one company.
That's why, I created 2 middle tables to handle it.
I've user table for all of my users.
I've account table to store subdomains with its creator.
And I've account_permissions to find out who are authorized to where.
So, when user1 comes to user2.example.com, I'm querying to my Account_permissions and if it has permission for user2.example.com, I let it go.
This way seems like sense.

If someone still have a similar problem and is not very expert in ROR, I suggest building the base app first and then making it multi_tenant using that video.
After building your app, you only need to install the apartment gem, then make a model which stores tenants information and then exclude it from being multi_tenanted.

Related

I want to handle Admin and Normal User login for different ends (Frontend + Backend) in ruby on rails with devise Authentication

I have a question related to ruby on rails. I have created two ends backend and frontend. I have added devise authentication gem for both backend and frontend. I have added user_type (1 = Admin, 2 = Normal User) attribute in user model and db. The problem is that i want to add authentication based on user type. If user type will be admin then can only access backend and if normal user then it will be able to access frontend.
I didn't find any way in devise gem to add check based on user type. The devise controllers code is very complex. Please suggest me best solution to add check based on user type.
Thanks in advance
You might want to adapt this tutorial to your needs (https://github.com/plataformatec/devise/wiki/How-To:-Add-an-Admin-Role)

Create a claim form for devise on rails by replicating the forget password form

I have a system in place where people need to claim accounts already set in a database by proving that they own the same email address. Every user in the user database has a password generated by Devise using the friendly token, so that people can still just login via omniauth plugins.
The current method I have planned out is to create a separate version of password#new (from Devise) as the 'claim' form, but I'm not too sure where to go from there. Do I have to create a whole new model? or can I just create a PasswordsController and config routes to go to a new view?

Rails & Devise - Autologin Across Subdomains

Setup
I have a Rails application where users register for an account, and a subdomain is created for them. They can then proceed to the subdomain and log in with their credentials. The workflow looks something like this:
User visits base domain fills out a form that with email/username/password and subdomain fields
From the submitted info, the server creates an account in the global/public database. Server then creates a database that will be specific to that particular subdomain/account, and stores the user record in it.
User is redirected to their subdomain, and asked to log in.
(note: to implement the separate "databases", I'm using postgres schemas, but that should be irrelevant.)
The question
My question involves step 3. I would like to redirect the user to their subdomain and log them in automatically instead of asking them to log in. However, I do not want to share a single session across all of the subdomains.
I would like to somehow securely transmit auto login request.
Possible Solution
I have considered using a single-use, random token that I would store in a cookie and in the users table. After the user successfully creates an account, he would be redirected to the subdomain. At that point the token would be consumed/destroyed and the user would be automatically logged in.
I would also need to have a short window for the token to be used before expiring.
Thoughts? Thanks!
I had the same issue, the possible solution you suggest does not work because the session is not shared between subdomains.
I solved it the following way (same idea you propossed, different implementation):
Create a new model (I called it LoginKey) that contains the user_id and a random SHA1 key.
When the user is authenticated at the parent domain (for example: mydomain.com/users/sign_in), a new LoginKey is created and the user is redirected to the corresponding subdomain to an action that I called login_with_key (for example: user_subdomain.mydomain.com/users/login_with_key?key=f6bb001ca50709efb22ba9b897d928086cb5d755322a3278f69be4d4daf54bbb)
Automatically log the user in with the key provided:
key = LoginKey.find_by_login_key(params[:key])
sign_in(key.user) unless key.nil?
Destroy the key:
key.destroy
I didn't like this solution 100%, I tried out a lot of different approaches that do not require a db record to be created, but always faced security concerns, and I think this one is safe.

A common user model , controller ,authentication and ability for multiple Rails apps

I have developed two rails applications app1 and app2, they have their own user controller and model and own ability.rb file and own devise gem. I want all of them share a common user controller and user model and ability.rb file so that anyone irrespective of the application goes through the same authentication system.
In this context I have read the post Rails: Devise Authentication from an ActiveResource call and How to add authentication before filter to a rails 3 apps with devise for user sign up and sign in?. But I am sorry, I could not figure out how to modify their individual routes.rb file so that all the authentication requests redirected to it and I would like to know if I have to make another application for only management of user for that purpose.
You might use omniauth gem to provide one application to manage its users through the second one (like a Facebook connect, for example). This app's sign in action would just be a redirect to the second one's sign in page.
In this case, however, you would have 2 different user tables, which might need synchronization, but for just a simple authentication that could work.

rails authentication for an API

I'm currently working on an application that in addition to the usual visual web application goop also will expose a few RESTful API services for use by outside applications. I am using Devise to manage user authentication but I'm struggling with how to "manually" authenticate a user given certain input.
The case I have is that I want a user of the API to log in w/o actually going to the visual log in screen etc. I want them to submit a username and password and then authenticate and sign them in in my API services.
I know that you can sign a user in using the sign_in method that Devise provides, but that seems to ignore authentication entirely. here's what I wanted to do explained in a bit more detail:
Assume a GET route called connect in the user controller. the controller is replacing entirely the Devise registrations controller, but not the session one. The URL to my service would be:
<server>/users/connect
and it would expect 'email', 'password' parameters in addition to some service specific and unimportant to my question goop.
What I want to know is how to implement something that is equivalent to the following pseudocode:
def connect
user = User.find_by_email(params[:email])
password = params[:password]
# here is the part I'm pseudo coding out
if user.is_valid_password(password)
...do my stuff...
end
render :json ...etc...
end
I have been unable to find a method in the Devise source to do this--it's so generalized in so many ways that I'm likely just missing it.
Anyone have any ideas? I'm hoping not to a) have to implement my own thing and b) not have to move away from Devise. It provides me with so much for the non-API services...
thanks!
I've left out th
Devise's token_authenticatable is the way to go for this. We've successfully used it many times to do api-based logins.
In config/initializers/devise.rb
config.token_authentication_key = :nameofyourapikeyhere
In user.rb:
devise … token_authenticatable, ...
In the above, you can name the api key anything and then have your route as /users/connect?apikey=whatever (using apikey as an example). In the database, it'll be authentication_token, but it'll work fine.
To clarify, if the user has an authentication_token and it's sent in the params (or it's alias- in the above example: apikey), they'll login.

Resources