module AuthlogicFacebook module Session def self.included(klass) klass.class_eval do extend Config include Methods end end module Config # REQUIRED # # Specify your api_key. # # * Default: nil # * Accepts: String def facebook_api_key(value=nil) rw_config(:facebook_api_key, value, nil) end alias_method :facebook_api_key=, :facebook_api_key # REQUIRED # # Specify your secret_key. # # * Default: nil # * Accepts: String def facebook_secret_key(value=nil) rw_config(:facebook_secret_key, value, nil) end alias_method :facebook_secret_key=, :facebook_secret_key # What user field should be used for the facebook UID? # # * Default: :facebook_uid # * Accepts: Symbol def facebook_uid_field(value=nil) rw_config(:facebook_uid_field, value, :facebook_uid) end alias_method :facebook_uid_field=, :facebook_uid_field # What extended permissions should be requested from the user? # # * Default: [] # * Accepts: Array of Strings def facebook_permissions(value=nil) rw_config(:facebook_permissions, value, []) end alias_method :facebook_permissions=, :facebook_permissions # Should a new user be automatically created if there is no user with # given facebook uid? # # * Default: false # * Accepts: Boolean def facebook_auto_register(value=true) rw_config(:facebook_auto_register, value, false) end alias_method :facebook_auto_register=, :facebook_auto_register end module Methods def self.included(klass) klass.class_eval do validate :validate_by_facebook, :if => :authenticating_with_facebook? end end # Clears out the block if we are authenticating with Facebook so that we # can redirect without a DoubleRender error. def save(&block) block = nil if redirecting_to_facebook? super(&block) end protected # Override this if you want only some requests to use facebook def authenticating_with_facebook? !authenticating_with_unauthorized_record? && !self.class.facebook_api_key.blank? && !self.class.facebook_secret_key.blank? end private def validate_by_facebook if facebook_callback? facebook_uid = facebook_session['uid'] self.attempted_record = klass.first(:conditions => {facebook_uid_field => facebook_uid}) if self.attempted_record || !facebook_auto_register? return !!self.attempted_record else self.attempted_record = klass.new self.attempted_record.send(:"#{facebook_uid_field}=", facebook_uid) if self.attempted_record.respond_to?(:before_connect) self.attempted_record.send(:before_connect, facebook_session) end return self.attempted_record.save(false) end else controller.redirect_to(facebook_login_url) return false end end def facebook_session return @facebook_session if defined?(@facebook_session) session_key = unverified_facebook_params['session_key'] uid = nil 10.times do params = {'session_key' => session_key, 'format' => 'JSON'} begin uid = MiniFB.call(self.class.facebook_api_key, self.class.facebook_secret_key, 'Users.getLoggedInUser', params) break rescue Errno::ECONNRESET # Try again end end if !uid raise 'Unable to reach Facebook after 10 tries.' end @facebook_session = {'uid' => uid, 'session_key' => session_key} end def unverified_facebook_params if defined?(@unverified_facebook_params) return @unverified_facebook_params end begin params = JSON.parse(controller.params['session'] || '') rescue JSON::JSONError params = {} end @unverified_facebook_params = params.is_a?(Hash) ? params : {} end def facebook_auto_register? self.class.facebook_auto_register end def facebook_callback? !unverified_facebook_params['uid'].blank? end def redirecting_to_facebook? authenticating_with_facebook? && !facebook_callback? end def facebook_uid_field self.class.facebook_uid_field end def facebook_login_url params = {'api_key' => self.class.facebook_api_key, 'req_perms' => self.class.facebook_permissions.join(','), 'next' => controller.request.url, 'v' => '1.0', 'connect_display' => 'popup', 'fbconnect' => 'true', 'return_session' => 'true'} url = 'http://www.facebook.com/login.php?' url << params.map{|k,v| "#{k}=#{CGI.escape(v)}"}.join('&') end end end end