Rails & Devise - Autologin Across Subdomains - ruby-on-rails

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.

Related

Rails: Multi-tenancy with Devise and Apartment gem

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.

Sitefinity one username for multiple user

To Whom it may concern
I would just like to know is possible to use one user name and it be used by multiple users at the same time on sitefinity without it kicking out a logged on user when another another user logs in using the same user name.
Kind Regards
If the user has a backend role then he will not be able to login concurrently from different machines/browsers. He will have to terminate the existing session before starting a new one.
Frontend users do not have this limitation.

Block another user from accessing the Rails application with my authentication

I have hosted a rails application, an online examination system. The users need to get registered to get access to the system. So each user will be provided with a unique combination of credentials. Let us assume my user-name/password is demo/demo123. I want my app to block another person logging in to the system even if he knows my credentials. Any solution for such scenario.
Thanks for any help :)-
Using Cookie would be a better solution. When the user gets registered create a Cookie value specific to the User and System and in encrypted format (for security reason) and save it in the database corresponding to that user. Check for this Cookie token while logging in. When the user clears the cookie, s(he) can request the Administrator to clear out the DB cookie for creating a new one.
Using IP will not be a better solution since in a network, there can be dynamic IP's allocated to the PC's.
You could record the user's IP address in the database when they first log in, and only allow logging in using the same credentials but from a new IP address after some waiting period of, say, 1 hour, or until the current examination is complete. That should prevent more than one user being logged in to the same user account within a short time period.
The user's IP address can be accessed in a Rails controller using request.remote_ip.
You could use the lock gem to add a password to the entire application.
First, add to your Gemfile.
gem 'lock'
Then
bundle install
Next, create your password
rails g lock:create_password_file yourpasswordhere
Finally, add lock your application controller, or whatever you'd like (see documentation).
ApplicationController < ActionController::Base
lock
end

Rails, Devise, and multiple domains

Let's say I have an application that's going to be accessed from completely different domains that all point at the same server*:
example.com, example.net, foobar.com, ...
I have a Devise based authentication system that's worked fine before. However, the goal is now to add HTTPS to the sign in system. The problem is, as it turns out, there is no way to host more than one HTTPS website on the same IP address**. To resolve this problem, I set up the login pages to always POST to https://secure.example.com. As far as I can tell, this is working fine. Devise seems to have no qualm with it. However, the tricky part is that the user now needs to be redirected to foobar.com, which also needs to understand that the user is logged in. I pass the site to return to in a hidden parameter in the login form, and the redirection works fine. I still have no way to inform foobar.com that the user is now logged in.
I've managed to set it up so that, upon being returned to foobar.com, it copies the user's session cookie for secure.example.com into a new cookie for foobar.com. This part is working fine. However, in the Rails console, the web requests for secure.example.com and foobar.com - with the same cookie sent for each - produce two completely different sessions and therefore, it's no wonder Devise acts like the user was never logged in to foobar.com
Does anyone know why this wouldn't work - why two identical web requests (only the domain of the request URI was different - I tried it in Firebug, too) would produce two completely different sessions in a Rails 3 app with different, yet consistent, session ids? More to the point, does anyone know how to MAKE this work?
* assume, for the purposes of this exercise, that this is unavoidable and the sites cannot be hosted all under different subdomains, and that the number of domains required is too great to get a separate IP address for each.
** unless they're subdomains and you have an *.example.com cert, but that's beside the point.
If you're already using Devise, I suggest you try using token authenticatable. You can generate a token for the user in question, redirect them with the token to sign in, and then quickly expire the token after they have signed in.
You could also try rolling your own OAuth provider with doorkeeper.

How do I let a user sign in from a different domain on Authlogic?

[This is slightly different than a previous question about having multiple domains share the same cookie. It seemed like there wasn't an easy way to do that.]
I have a application at application.com. A customer has app.customer.com pointed at my site on Heroku, and I have everything set up so that it renders a specific version of app correctly. The issue is that I want a user at app.customer.com to be able to login. I believe authlogic is now setting the cookie on application.com, so while it verifies the credentials, no session on customer.com is ever created.
Since cookies cannot be shared across domains, you probably need to save an identifier in a database and also pass it through the url so when the client browser hits the new domain, it sends the token for the new domain session to see and match.
It should be a long cryptographically safe token, like a UUID to keep it from being guessed by attackers.
I'm not sure how the authlogic piece fits in though.

Resources