module Authlogic module Session # Handles all authentication that deals with cookies, such as persisting, saving, and destroying. module Cookies def self.included(klass) klass.class_eval do extend Config include InstanceMethods persist :persist_by_cookie after_save :save_cookie after_destroy :destroy_cookie end end # Configuration for the cookie feature set. module Config # The name of the cookie or the key in the cookies hash. Be sure and use a unique name. If you have multiple sessions and they use the same cookie it will cause problems. # Also, if a id is set it will be inserted into the beginning of the string. Exmaple: # # session = UserSession.new # session.cookie_key => "user_credentials" # # session = UserSession.new(:super_high_secret) # session.cookie_key => "super_high_secret_user_credentials" # # * Default: "#{klass_name.underscore}_credentials" # * Accepts: String def cookie_key(value = nil) rw_config(:cookie_key, value, "#{klass_name.underscore}_credentials") end alias_method :cookie_key=, :cookie_key # If sessions should be remembered by default or not. # # * Default: false # * Accepts: Boolean def remember_me(value = nil) rw_config(:remember_me, value, false) end alias_method :remember_me=, :remember_me # The length of time until the cookie expires. # # * Default: 3.months # * Accepts: Integer, length of time in seconds, such as 60 or 3.months def remember_me_for(value = :_read) rw_config(:remember_me_for, value, 3.months, :_read) end alias_method :remember_me_for=, :remember_me_for # Should the cookie be set as secure? If true, the cookie will only be sent over SSL connections # # * Default: false # * Accepts: Boolean def secure(value = nil) rw_config(:secure, value, false) end alias_method :secure=, :secure # Should the cookie be set as httponly? If true, the cookie will not be accessable from javascript # # * Default: false # * Accepts: Boolean def httponly(value = nil) rw_config(:httponly, value, false) end alias_method :httponly=, :httponly end # The methods available for an Authlogic::Session::Base object that make up the cookie feature set. module InstanceMethods # Allows you to set the remember_me option when passing credentials. def credentials=(value) super values = value.is_a?(Array) ? value : [value] case values.first when Hash self.remember_me = values.first.with_indifferent_access[:remember_me] if values.first.with_indifferent_access.key?(:remember_me) else r = values.find { |value| value.is_a?(TrueClass) || value.is_a?(FalseClass) } self.remember_me = r if !r.nil? end end # Is the cookie going to expire after the session is over, or will it stick around? def remember_me return @remember_me if defined?(@remember_me) @remember_me = self.class.remember_me end # Accepts a boolean as a flag to remember the session or not. Basically to expire the cookie at the end of the session or keep it for "remember_me_until". def remember_me=(value) @remember_me = value end # See remember_me def remember_me? remember_me == true || remember_me == "true" || remember_me == "1" end # How long to remember the user if remember_me is true. This is based on the class level configuration: remember_me_for def remember_me_for return unless remember_me? self.class.remember_me_for end # When to expire the cookie. See remember_me_for configuration option to change this. def remember_me_until return unless remember_me? remember_me_for.from_now end # If the cookie should be marked as secure (SSL only) def secure return @secure if defined?(@secure) @secure = self.class.secure end # Accepts a boolean as to whether the cookie should be marked as secure. If true the cookie will only ever be sent over an SSL connection. def secure=(value) @secure = value end # See secure def secure? secure == true || secure == "true" || secure == "1" end # If the cookie should be marked as httponly (not accessable via javascript) def httponly return @httponly if defined?(@httponly) @httponly = self.class.httponly end # Accepts a boolean as to whether the cookie should be marked as httponly. If true, the cookie will not be accessable from javascript def httponly=(value) @httponly = value end # See httponly def httponly? httponly == true || httponly == "true" || httponly == "1" end private def cookie_key build_key(self.class.cookie_key) end def cookie_credentials controller.cookies[cookie_key] && controller.cookies[cookie_key].split("::") end # Tries to validate the session from information in the cookie def persist_by_cookie persistence_token, record_id = cookie_credentials if !persistence_token.nil? record = record_id.nil? ? search_for_record("find_by_persistence_token", persistence_token) : search_for_record("find_by_#{klass.primary_key}", record_id) self.unauthorized_record = record if record && record.persistence_token == persistence_token valid? else false end end def save_cookie controller.cookies[cookie_key] = { :value => "#{record.persistence_token}::#{record.send(record.class.primary_key)}", :expires => remember_me_until, :secure => secure, :httponly => httponly, :domain => controller.cookie_domain } end def destroy_cookie controller.cookies.delete cookie_key, :domain => controller.cookie_domain end end end end end