I am trying to get up a simple authentication system with Rails' Restful-Authentication plugin, and am just wondering how it works, b/c I can't seem to figure out what the requirements are for cookies, and how to make it so the browser always remembers you (for 6+ months).
Few questions:
1) How do you do remember_me's for ruby's restful_authentication? I can't seem to find a good one-liner to solve this problem...
If a user signs up and checks "Remember Me", how does the rails application get the session/cookie without the user doing anything but going to the page the next time they go to the page, say 3 months later?
2) Do I need to send some sort of info to the server, like their IP address or something? What is cookies[:auth_token]
, where is that defined?
The goal is: I don't want them to have to enter their email/password again, like how StackOverflow works :)
Here's what we're doing (largely taken from authenticated system) ... this is the controller method that handles login that we're running...
def login
if logged_in?
flash[:notice] = "You are already logged in."
redirect_to "/" and return
end
unless request.post?
render :layout => 'task' and return
end
self.current_user = User.authenticate(params[:login], params[:password])
if logged_in?
if params[:remember_me].to_i == 1
self.current_user.remember_me
cookies[:auth_token] = {:domain => "#{DOMAIN}", :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
else
self.current_user.forget_me
cookies.delete(:auth_token, :domain => "#{DOMAIN}")
cookies[:auth_token] = nil
end
current_user.last_seen_at = Time.now
current_user.save
session[:notice] = "You logged in successfully"
flash[:notice] = "You logged in successfully"
redirect_back_or_default(:controller => 'dashboard') and return
#redirect_back_or_default(:controller => 'index', :action => 'index') and return
else
if $failed_login_counter.add_attempt(params[:login]) > MAXIMUM_LOGIN_ATTEMPTS
logger.info("login rate limiter kicking in, #{MAXIMUM_LOGIN_ATTEMPTS} login attempts failed")
redirect_to "/denied.html" and return
end
flash[:error] = "Unable to authenticate username and password"
render(:layout => 'task') and return
end
end
And use this for logout
def logout
current_user.last_seen_at = Time.now
current_user.save
self.current_user.forget_me if logged_in?
cookies.delete(:auth_token, :domain => "#{DOMAIN}")
reset_session
flash[:notice] = "You have been logged out."
#redirect_to :back
redirect_back_or_default(:controller => 'index', :action => 'index') and return
end
Then - in your application.rb you'll need something like:
before_filter :login_from_cookie
def login_from_cookie
return unless cookies[:auth_token] && !logged_in?
user = User.find_by_remember_token(cookies[:auth_token])
if user && user.remember_token?
user.remember_me
self.current_user = user
cookies[:auth_token] = { :domain => "#{DOMAIN}", :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
flash[:notice] = "#{self.current_user.login}, you have logged in successfully"
end
end
And - in your User model have some methods like this:
# Encrypts some data with the salt.
def self.encrypt(password, salt)
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
end
# Encrypts the password with the user salt
def encrypt(password)
self.class.encrypt(password, salt)
end
def remember_token?
remember_token_expires_at && Time.now.utc < remember_token_expires_at
end
# These create and unset the fields required for remembering users between browser closes
def remember_me
self.remember_token_expires_at = 2.weeks.from_now.utc
self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")
save(false)
end
def forget_me
self.remember_token_expires_at = nil
self.remember_token = nil
save(false)
end
I'm honestly not sure aboout that particular implementation. But a common RESTful method of authentication is to pass a hashed version of the user/password with each request as a header. Alternatively you can use a hashed cookie value as a header.
I've also seen hybrid systems that involve both. You pass in the session, if you know it, in addition to the user/pass. Then server side if the session is valid it uses that and can cache the session - > user relationship for performance. If the session is invalid, it attempts to authenticate using the user/pass.
In this type of system you'd pass the session back on the response as a header.
Of course that's just a quick rundown of how a system might work, not how ruby's library does.
You can find here a whole tutorial about restful authentication.
http://railsforum.com/viewtopic.php?id=14216&p=13