Reputation: 1708
I have restful_authentication in my app. With that came a sessions controller. Everything has been working weel for me except one thing. When the window closes and is then reopened on the same machine, the apps state is saved including the logged in user. I require my app to log the user out when the browser window is closed. But I have no idea how to make it happen. Is there something that is causing it to save, or do I need to add something to kill it?
Here is the sessions controller:
# This controller handles the login/logout function of the site.
class SessionsController < ApplicationController
# render new.rhtml
def new
end
def create
logout_keeping_session!
user = User.authenticate(params[:login], params[:password])
if user
unless user.active?
# ensures "deleted" users can't login
user = nil
end
end
if user
# Protects against session fixation attacks, causes request forgery
# protection if user resubmits an earlier form using back
# button. Uncomment if you understand the tradeoffs.
# reset_session
self.current_user = user
new_cookie_flag = (params[:remember_me] == "1")
handle_remember_cookie! new_cookie_flag
redirect_back_or_default('/')
else
note_failed_signin
@login = params[:login]
@remember_me = params[:remember_me]
render :action => 'new'
end
end
def destroy
logout_killing_session!
redirect_back_or_default('/', :notice => "You have been logged out.")
end
protected
# Track failed login attempts
def note_failed_signin
flash.now[:alert] = "Couldn't log you in as '#{params[:login]}'"
logger.warn "Failed login for '#{params[:login]}' from #{request.remote_ip} at #{Time.now.utc}"
end
end
Here is the authenticatedSystem module (obviously unrelated material removed)
module AuthenticatedSystem
protected
# Returns true or false if the user is logged in.
# Preloads @current_user with the user model if they're logged in.
def logged_in?
!!current_user
end
# Accesses the current user from the session.
# Future calls avoid the database because nil is not equal to false.
def current_user
@current_user ||= (login_from_session || login_from_basic_auth || login_from_cookie) unless @current_user == false
end
# Store the given user id in the session.
def current_user=(new_user)
session[:user_id] = new_user ? new_user.id : nil
@current_user = new_user || false
end
def authorized?(action = action_name, resource = nil)
logged_in?
end
def login_required
authorized? || access_denied
end
def access_denied
respond_to do |format|
format.html do
store_location
redirect_to new_session_path
end
# format.any doesn't work in rails version < http://dev.rubyonrails.org/changeset/8987
# Add any other API formats here. (Some browsers, notably IE6, send Accept: */* and trigger
# the 'format.any' block incorrectly. See http://bit.ly/ie6_borken or http://bit.ly/ie6_borken2
# for a workaround.)
format.any(:json, :xml) do
request_http_basic_authentication 'Web Password'
end
end
end
# Store the URI of the current request in the session.
#
# We can return to this location by calling #redirect_back_or_default.
def store_location
session[:return_to] = request.fullpath
end
# Redirect to the URI stored by the most recent store_location call or
# to the passed default. Set an appropriately modified
# after_filter :store_location, :only => [:index, :new, :show, :edit]
# for any controller you want to be bounce-backable.
def redirect_back_or_default(default, options = {})
redirect_to((session[:return_to] || default), options)
session[:return_to] = nil
end
# Inclusion hook to make #current_user and #logged_in?
# available as ActionView helper methods.
def self.included(base)
base.send :helper_method, :current_user, :logged_in?, :authorized? if base.respond_to? :helper_method
end
#
# Login
#
# Called from #current_user. First attempt to login by the user id stored in the session.
def login_from_session
self.current_user = User.find_by_id(session[:user_id]) if session[:user_id]
end
# Called from #current_user. Now, attempt to login by basic authentication information.
def login_from_basic_auth
authenticate_with_http_basic do |login, password|
self.current_user = User.authenticate(login, password)
end
end
#
# Logout
#
# Called from #current_user. Finaly, attempt to login by an expiring token in the cookie.
# for the paranoid: we _should_ be storing user_token = hash(cookie_token, request IP)
def login_from_cookie
user = cookies[:auth_token] && User.find_by_remember_token(cookies[:auth_token])
if user && user.remember_token?
self.current_user = user
handle_remember_cookie! false # freshen cookie token (keeping date)
self.current_user
end
end
# This is ususally what you want; resetting the session willy-nilly wreaks
# havoc with forgery protection, and is only strictly necessary on login.
# However, **all session state variables should be unset here**.
def logout_keeping_session!
# Kill server-side auth cookie
@current_user.forget_me if @current_user.is_a? User
# @current_user = false # not logged in, and don't do it for me
kill_remember_cookie! # Kill client-side auth cookie
session[:user_id] = nil # keeps the session but kill our variable
# explicitly kill any other session variables you set
end
# The session should only be reset at the tail end of a form POST --
# otherwise the request forgery protection fails. It's only really necessary
# when you cross quarantine (logged-out to logged-in).
def logout_killing_session!
logout_keeping_session!
reset_session
end
Upvotes: 1
Views: 1558
Reputation: 11198
This happens when your browser's (Firefox, in my case) start up preference are set to 'show my windows and tabs from last time'. It also happens when you 'save your tabs for the next time it starts' when closing the browser (all its windows). In these cases, the browser remembers the sesssion, i.e. it does not delete the session cookie as it would normally do.
Upvotes: 3
Reputation: 40277
When the user is re-opening their browser you are authenticating them from a cookie. Try removing login_from_cookie
from
def current_user
@current_user ||= (login_from_session || login_from_basic_auth) unless @current_user == false
end
Upvotes: 1