Devise: How to use remember_me cookie after user sign out? - ruby-on-rails

I'm working on a Rails 4.2 Application and using devise gem for authentication.
For remember_me feature, devise generates a cookie remember_user_token which gets destroy after sign_out.
Is there a way such that Devise should not destroy remember_user_token ?
I tried to false the below config in the initializer
config.expire_all_remember_me_on_sign_out = false
But it didn't help.
I need that cookie after sign-out such that it will populate the login form.
Please help.
Thanks

Coupling authentication with form pre-filling isn't necessarily a good idea. You can store the login in a cookie upon successful login. You can override the create method in your SessionsController, call super to call Devise::SessionsController#create and pass it a block. The block will be executed after a successful log in and will receive the user as a parameter.
class SessionsController < Devise::SessionsController
def create
super do |user|
cookies[:login] = user.login
end
end
end

Here is the low down on cookie store. First off, everything in a cookie is there permanently once it's set or until the user deletes the cookie manually somehow. This means, that if you set user_id and user_group_id, it's there for good in the cookie until updated or deleted. This is different from a session since the session is like ram on a computer, once the browser is closed, the session closes with it as well as all of it's data.
So, this means that when you log out your user, you need to specify that their cookie empties anything you don't wan't it to have. When your user logs in, you set anything that you want the user to have while they are logged in. So, since the session and cookie are separate things completely, they never interact together unless you choose to make them. So your session will never dump its self into the cookie store unless you make it do that.
Every time your users go to your site, you could have a single handshake that makes sure that the cookie matches the db if necessary. Otherwise, you could have differing data what only gets updated on login or what not and without the handshake, the user would have to keep logging in to make sure they are still valid which defeats the purpose of having a cookie in the first place.
The downside of client side cookie storage is security concerns. Depending on how you use the cookie to store data, a person could hijack somebodies cookie on your site and pretend they are them. This can be avoided by careful design, but just assume that whatever is in your cookie store is fair game to everybody so use it carefully and for only non secret data.
Hope this helps!

Related

How does session and cookie work in Rails 4?

As I understand one of the strategies to store sessions is store it in the cookie. There is one thing I don't understand from the docs:
To prevent session hash tampering, a digest is calculated from the
session with a server-side secret and inserted into the end of the
cookie.
What does this mean? How do they prevent that, if I get a cookie from another user, and I use it in my browser, I can't pretend I am the other user? I guess I don't understand what session hash tampering means.
How do they prevent that, if I get a cookie from another user, and I
use it in my browser, I can't pretend I am the other user?
This is called session hijacking, and is covered in http://guides.rubyonrails.org/security.html#session-hijacking. The recommended way to to mitigate this is by "always forcing SSL connection in your application config file", like so:
config.force_ssl = true
The whole http://guides.rubyonrails.org/security.html is definitely worth a read, for more goodness like this.

Implementing a simple sign up/log in system in Rails using cookies

I'm fairly new to Rails and I'm trying to implement a really basic user authentication system. My main problem right now is that I do not have a clue what's a good design for this.
What I have now
My Rails app is not a website; it's a webservice that communicates with an iPhone app. All communication is in JSON.
When a user signs up, the app sends a POST request to /users.json with name and password as JSON in the HTTP body. Rails then saves the user id in a cookie:
cookies.signed[:user_id] = #user.id
When a user logs out, the app sends a POST request to /logout.json. Rails gets the user id from the cookie, updates the database and deletes the cookie.
Later, when the user wants to log in again, the app sends a POST request to /login.json with name and password as JSON in the HTTP body. Rails sets the cookie again.
My question(s)
Is this design RESTful? Because login and logout aren't resources.
Is it secure to use cookies like this? I'm planning to use SSL.
Is there a better way to do this?
Tt's a very basic approach
A more RESTful way:
Create a controller called sessions with a create and destroy action. Throw the login/logout out of your head and start thinking in sessions. A login is just a username/password combination. When you login you create/start a session, when you log out you destroy the session. The login is not affected.
Instead of using cookies.signed[:user_id] you should use session[:user_id]
SSL is a big plus, because the password can't be sniffed through the network.
And there are lots of out-of-the-box authentication systems.
My favourite is Devise:
https://github.com/plataformatec/devise
http://asciicasts.com/episodes/209-introducing-devise
http://railscasts.com/episodes/209-introducing-devise
there is a nice screencast ryan bates did on authentication from scratch
http://railscasts.com/episodes/250-authentication-from-scratch
No, for the reason you stated.
No, you need to pass a second parameter to encrypt the user's id.
Well, you could use a gem, there's a few about.
If you do want to code it yourself, then I'd recommend looking at a tutorial. The one at railstutorial.org is pretty good.
The basic idea is to create a user model with an encrypted password, which uses a salt formed from the current time and password. You then create a sessions controller and make it RESTful, but using cookies instead of another resource. Make sure to encrypt the cookie with the salt as well:
cookies.permanent.signed[:remember_token] = [user.id, user.salt]

Rails Login Reset Session

Is it best practice to call reset_session when a user successfully signs in and to call it again when a user signs out? Are there any side effects/problems to doing this?
The Ruby on Rails Security Guide recommends resetting the session id upon successful authentication to protect against session fixation vulnerabilities. Essentially, session fixation involves an attacker setting your session id (or some other method of being able to know what the id is when you hit the login page), and, upon your successful authentication, the attacker sets a cookie for their own browser using your session id and are subsequently authenticated as you. Resetting the session id upon successful authentication completely mitigates such a vulnerability. Some sample code in your create action might look like:
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
old_values = session.to_hash
reset_session
session.update old_values.except('session_id')
session[:athlete_id] = athlete.id
redirect_to root_url, notice: "Authentication successful!"
else
flash.now.alert = "Invalid credentials"
render "new"
end
end
Note that it's important to duplicate the session before resetting it if there is any data you wish to preserve.
As far as calling reset_session on logout, yes, this is also best practice as well.
That's really going to depend on how you store things in session and how you want the security to operate.
Reset session will drop everything from the user's session, so if they hop back into the login screen and sign back in but still had (for example) a shopping cart stored to their session, you'll clear that which may be undesirable.
If you're not storing any data you feel the users might want to hold on to, I know of no reason clearing the session before processing a login attempt would hurt at all, an on sign outs I recommend it.
I consider it good practice to reset the session when a user logs in. That way, malicious individuals can't sniff out their session cookie before a client's connection is encrypted and still use it after they use the sign in form over https. Try:
temp = session
reset_session
session.reverse_merge!(temp)
This way, session gets the new values generated by reset_session, but any other session variables remain intact.
A lot of the answers here haven't aged well due to the Rails API changing so I'll just leave one here that works as of Rails 5.0 at least.
As others have noted the Rails Security Guide recommends calling reset_session on login to avoid session fixation attacks.
You may want your session cleared on login but if you just want to change the session id and keep everything else (i.e. no side-effects) you can do it like this:
def mitigate_session_fixation
old_values = session.to_hash
reset_session
session.update old_values.except('session_id')
end

Specify Cookie Domain in Authlogic When Session Is Created

Is it possible to set the cookie domain to something other than the current domain when a session is created with Authlogic?
When a new account is created from our signup domain, I'd like to redirect the user to their subdomain account and log the user in.
Current controller:
def create
#account = Account.new(params[:account])
if #account.save
#user_session = #account.user_sessions.create(#account.users.first)
# I'd like the cookie domain to be [#account.subdomain, APP_CONFIG[:domain]].join(".")
redirect_to admin_root_url(:host => [#account.subdomain, APP_CONFIG[:domain]].join("."))
else
render 'new'
end
end
If you do:
config.action_controller.session[:domain] = '.YOURDOMAIN.COM'
in your production.rb file, that will allow you to have everyone logged in on all subdomains of your subdomain. If you then add a filter (or whatever, but I use a filter so I know that works) that checks that someone is actually using the right domain before you show controller stuff, it works pretty well.
As an example, you could store the appropriate subdomain for the session as a session variable and give people link options to their specific things if they were on your main domain or looking at a page on someone else's subdomain.
This seems to be the general pattern for doing this sort of thing -- if you set a cookie specific to the subdomain otherwise you won't be able to tell when they've logged in to the main site. I also have a 'users_domain?' helper that ends up getting called occasionally in views when I do this.
If you don't want to have those sorts of common web design patterns, wesgarrion's single use -> session creation on subdomain is also a way to go. I just thought I'd mention this as a design / interaction / code issue.
If you want to log them in on the subdomain, you can use Authlogic's single use token.
Check out the Params module for an example on logging in with the single use token.
Naturally, your action will log them in and create their session (on the subdomain) so they don't have to re-authenticate for the next request.
There are options to set the domain for the cookie in process_cgi() and session(), but I don't see a way to set those per-request in Authlogic. The authlogic mailing list is pretty responsive, though, and this seems like a pretty standard use-case that someone there would have tried and figured out. And uh, I saw your note on the google group, so never mind that.
If you have an application with multiple subdomains and don't want session cookies to be shared among them, or worse - have a top-level .domain session cookie with the same session_key floating around alongside your subdomain session cookie (Rails will keep one and toss the other - I believe simply based on the order in the request header) - you can use the dispatcher hooks to force the session cookie to subdomains.
Include the hook in ActionController from an extension.
base.send :after_dispatch, :force_session_cookies_to_subdomains
Set the domain this in your after_ dispatch hook.
#env['rack.session.options'] = #env['rack.session.options'].merge(:domain => 'my_sub_domain' end)
For us, we look at the #env[HTTP_HOST] to determine what [my_sub_domain] should be.
With this approach, the user's login must occur at the subdomain for the browser to accept the subdomain'ed cookie (unless using a pattern like the Authlogic Params to propagate to the next request against the subdomain).
Note: The browser will reject the subdomain'ed cookie when the request comes from the higher level domain. For us, this isn't a bad thing - it results in the same outcome that we require, that a top level session cookie doesn't get created and later sent to subdomains.
Another approach to a similar end might be to force a cookie to not be set when not from a subdomain. Not spending much time on it, the way I was able to accomplish this was -
request.env["rack.session"] = ActionController::Session::AbstractStore::SessionHash.new(self, request.env)
in an after filter in ApplicationController.

Implementation of "Remember me" in a Rails application

My Rails-app has a sign in box with a "remember me" checkbox. Users who check that box should remain logged in even after closing their browser. I'm keeping track of whether users are logged in by storing their id in the user's session.
But sessions are implemented in Rails as session cookies, which are not persistent. I can make them persistent:
class ApplicationController < ActionController::Base
before_filter :update_session_expiration_date
private
def update_session_expiration_date
options = ActionController::Base.session_options
unless options[:session_expires]
options[:session_expires] = 1.year.from_now
end
end
end
But that seems like a hack, which is surprising for such common functionality. Is there any better way?
Edit
Gareth's answer is pretty good, but I would still like an answer from someone familiar with Rails 2 (because of it's unique CookieSessionStore).
You should almost certainly not be extending the session cookie to be long lived.
Although not dealing specifically with rails this article goes to some length to explain 'remember me' best practices.
In summary though you should:
Add an extra column to the user table to accept a large random value
Set a long lived cookie on the client which combines the user id and the random value
When a new session starts, check for the existence of the id/value cookie and authenticate the new user if they match.
The author also recommends invalidating the random value and resetting the cookie at every login. Personally I don't like that as you then can't stay logged into a site on two computers. I would tend to make sure my password changing function also reset the random value thus locking out sessions on other machines.
As a final note, the advice he gives on making certain functions (password change/email change etc) unavailable to auto authenticated sessions is well worth following but rarely seen in the real world.
I have spent a while thinking about this and came to some conclusions. Rails session cookies are tamper-proof by default, so you really don't have to worry about a cookie being modified on the client end.
Here is what I've done:
Session cookie is set to be long-lived (6 months or so)
Inside the session store
An 'expires on' date that is set to login + 24 hours
user id
Authenticated = true so I can allow for anonymous user sesssions (not dangerous because of the cookie tamper protection)
I add a before_filter in the Application Controller that checks the 'expires on' part of the session.
When the user checks the "Remember Me" box, I just set the session[:expireson] date to be login + 2 weeks. No one can steal the cookie and stay logged in forever or masquerade as another user because the rails session cookie is tamper-proof.
I would suggest that you either take a look at the RESTful_Authentication plug in, which has an implementation of this, or just switch your implementation to use the RESTful Authentication_plugin. There is a good explanation about how to use this plug in at Railscasts:
railscasts #67 restful_authentication
Here is a link to the plugin itself
restful_authentication
The restful_authentication plugin has a good implementation of this:
http://agilewebdevelopment.com/plugins/restful_authentication
Note that you don't want to persist their session, just their identity. You'll create a fresh session for them when they return to your site. Generally you just assign a GUID to the user, write that to their cookie, then use it to look them up when they come back. Don't use their login name or user ID for the token as it could easily be guessed and allow crafty visitors to hijack other users' accounts.
This worked like a charm for me:
http://squarewheel.wordpress.com/2007/11/03/session-cookie-expiration-time-in-rails/
Now my CookieStore sessions expire after two weeks, whereby the user must submit their login credentials again in order to be persistently logged-in for another two weeks.
Bascially, it's as simple as:
including one file in vendor/plugins directory
set session expiry value in application controller using just one line
I would go for Devise for a brilliant authentication solution for rails.

Resources