The Strategy is the base unit of OmniAuth's ability to wrangle multiple providers. Each strategy provided by OmniAuth includes this mixin to gain the default functionality necessary to be compatible with the OmniAuth library.
# File lib/omniauth/strategy.rb, line 11 def self.included(base) OmniAuth.strategies << base base.extend ClassMethods base.class_eval do option :setup, false option :skip_info, false end end
Initializes the strategy by passing in the Rack endpoint, the unique URL segment name for this strategy, and any additional arguments. An `options` hash is automatically created from the last argument if it is a hash.
@param app [Rack application] The application on which this middleware is applied.
@overload new(app, options = {})
If nothing but a hash is supplied, initialized with the supplied options overriding the strategy's default options via a deep merge.
@overload new(app, *args, options = {})
If the strategy has supplied custom arguments that it accepts, they may will be passed through and set to the appropriate values.
@yield [Options] Yields options to block for further configuration.
# File lib/omniauth/strategy.rb, line 130 def initialize(app, *args, &block) @app = app @env = nil @options = self.class.default_options.dup options.deep_merge!(args.pop) if args.last.is_a?(Hash) options.name ||= self.class.to_s.split('::').last.downcase self.class.args.each do |arg| options[arg] = args.shift end # Make sure that all of the args have been dealt with, otherwise error out. raise ArgumentError, "Received wrong number of arguments. #{args.inspect}" unless args.empty? yield options if block_given? end
# File lib/omniauth/strategy.rb, line 333 def auth_hash hash = AuthHash.new(:provider => name, :uid => uid) hash.info = info unless skip_info? hash.credentials = credentials if credentials hash.extra = extra if extra hash end
Duplicates this instance and runs call! on it. @param [Hash] The Rack environment.
# File lib/omniauth/strategy.rb, line 163 def call(env) dup.call!(env) end
The logic for dispatching any additional actions that need to be taken. For instance, calling the request phase if the request path is recognized.
@param env [Hash] The Rack environment.
# File lib/omniauth/strategy.rb, line 172 def call!(env) raise OmniAuth::NoSessionError.new("You must provide a session to use OmniAuth.") unless env['rack.session'] @env = env @env['omniauth.strategy'] = self if on_auth_path? return mock_call!(env) if OmniAuth.config.test_mode return options_call if on_auth_path? && options_request? return request_call if on_request_path? && OmniAuth.config.allowed_request_methods.include?(request.request_method.downcase.to_sym) return callback_call if on_callback_path? return other_phase if respond_to?(:other_phase) @app.call(env) end
# File lib/omniauth/strategy.rb, line 400 def call_app!(env = @env) @app.call(env) end
Performs the steps necessary to run the callback phase of a strategy.
# File lib/omniauth/strategy.rb, line 219 def callback_call setup_phase log :info, "Callback phase initiated." @env['omniauth.origin'] = session.delete('omniauth.origin') @env['omniauth.origin'] = nil if env['omniauth.origin'] == '' @env['omniauth.params'] = session.delete('omniauth.params') || {} callback_phase end
# File lib/omniauth/strategy.rb, line 384 def callback_path options[:callback_path].is_a?(String) ? options[:callback_path] : (custom_path(:request_path) || "#{path_prefix}/#{name}/callback") end
# File lib/omniauth/strategy.rb, line 361 def callback_phase self.env['omniauth.auth'] = auth_hash call_app! end
# File lib/omniauth/strategy.rb, line 419 def callback_url full_host + script_name + callback_path + query_string end
# File lib/omniauth/strategy.rb, line 325 def credentials merge_stack(self.class.credentials_stack(self)) end
# File lib/omniauth/strategy.rb, line 392 def current_path request.path_info.downcase.sub(/\/$/,'') end
# File lib/omniauth/strategy.rb, line 370 def custom_path(kind) if options[kind].respond_to?(:call) result = options[kind].call(env) return nil unless result.is_a?(String) result else options[kind] end end
# File lib/omniauth/strategy.rb, line 329 def extra merge_stack(self.class.extra_stack(self)) end
# File lib/omniauth/strategy.rb, line 454 def fail!(message_key, exception = nil) self.env['omniauth.error'] = exception self.env['omniauth.error.type'] = message_key.to_sym self.env['omniauth.error.strategy'] = self if exception log :error, "Authentication failure! #{message_key}: #{exception.class.to_s}, #{exception.message}" else log :error, "Authentication failure! #{message_key} encountered." end OmniAuth.config.on_failure.call(self.env) end
# File lib/omniauth/strategy.rb, line 404 def full_host case OmniAuth.config.full_host when String OmniAuth.config.full_host when Proc OmniAuth.config.full_host.call(env) else uri = URI.parse(request.url.gsub(/\?.*$/,'')) uri.path = '' #sometimes the url is actually showing http inside rails because the other layers (like nginx) have handled the ssl termination. uri.scheme = 'https' if ssl? uri.to_s end end
# File lib/omniauth/strategy.rb, line 321 def info merge_stack(self.class.info_stack(self)) end
# File lib/omniauth/strategy.rb, line 148 def inspect "#<#{self.class.to_s}>" end
Direct access to the OmniAuth logger, automatically prefixed with this strategy's name.
@example
log :warn, "This is a warning."
# File lib/omniauth/strategy.rb, line 157 def log(level, message) OmniAuth.logger.send(level, "(#{name}) #{message}") end
This is called in lieu of the normal request process in the event that OmniAuth has been configured to be in test mode.
# File lib/omniauth/strategy.rb, line 262 def mock_call!(env) return mock_request_call if on_request_path? return mock_callback_call if on_callback_path? call_app! end
# File lib/omniauth/strategy.rb, line 281 def mock_callback_call setup_phase mocked_auth = OmniAuth.mock_auth_for(name.to_s) if mocked_auth.is_a?(Symbol) fail!(mocked_auth) else @env['omniauth.auth'] = mocked_auth @env['omniauth.params'] = session.delete('omniauth.params') || {} @env['omniauth.origin'] = session.delete('omniauth.origin') @env['omniauth.origin'] = nil if env['omniauth.origin'] == '' call_app! end end
# File lib/omniauth/strategy.rb, line 268 def mock_request_call setup_phase session['omniauth.params'] = request.params if request.params['origin'] @env['rack.session']['omniauth.origin'] = request.params['origin'] elsif env['HTTP_REFERER'] && !env['HTTP_REFERER'].match(/#{request_path}$/) @env['rack.session']['omniauth.origin'] = env['HTTP_REFERER'] end redirect(script_name + callback_path + query_string) end
# File lib/omniauth/strategy.rb, line 435 def name options.name end
Returns true if the environment recognizes either the request or callback path.
# File lib/omniauth/strategy.rb, line 231 def on_auth_path? on_request_path? || on_callback_path? end
# File lib/omniauth/strategy.rb, line 243 def on_callback_path? if options.callback_path.respond_to?(:call) options.callback_path.call(env) else on_path?(callback_path) end end
# File lib/omniauth/strategy.rb, line 251 def on_path?(path) current_path.casecmp(path) == 0 end
# File lib/omniauth/strategy.rb, line 235 def on_request_path? if options.request_path.respond_to?(:call) options.request_path.call(env) else on_path?(request_path) end end
Responds to an OPTIONS request.
# File lib/omniauth/strategy.rb, line 188 def options_call verbs = OmniAuth.config.allowed_request_methods.map(&:to_s).map(&:upcase).join(', ') return [ 200, { 'Allow' => verbs }, [] ] end
# File lib/omniauth/strategy.rb, line 255 def options_request? request.request_method == 'OPTIONS' end
# File lib/omniauth/strategy.rb, line 366 def path_prefix options[:path_prefix] || OmniAuth.config.path_prefix end
# File lib/omniauth/strategy.rb, line 396 def query_string request.query_string.empty? ? "" : "?#{request.query_string}" end
# File lib/omniauth/strategy.rb, line 439 def redirect(uri) r = Rack::Response.new if options[:iframe] r.write("<script type='text/javascript' charset='utf-8'>top.location.href = '#{uri}';</script>") else r.write("Redirecting to #{uri}...") r.redirect(uri) end r.finish end
# File lib/omniauth/strategy.rb, line 431 def request @request ||= Rack::Request.new(@env) end
Performs the steps necessary to run the request phase of a strategy.
# File lib/omniauth/strategy.rb, line 194 def request_call setup_phase log :info, "Request phase initiated." #store query params from the request url, extracted in the callback_phase session['omniauth.params'] = request.params if options.form.respond_to?(:call) log :info, "Rendering form from supplied Rack endpoint." options.form.call(env) elsif options.form log :info, "Rendering form from underlying application." call_app! else if request.params['origin'] env['rack.session']['omniauth.origin'] = request.params['origin'] elsif env['HTTP_REFERER'] && !env['HTTP_REFERER'].match(/#{request_path}$/) env['rack.session']['omniauth.origin'] = env['HTTP_REFERER'] end request_phase end end
# File lib/omniauth/strategy.rb, line 380 def request_path options[:request_path].is_a?(String) ? options[:request_path] : "#{path_prefix}/#{name}" end
@abstract This method is called when the user is on the request path. You should perform any information gathering you need to be able to authenticate the user in this phase.
# File lib/omniauth/strategy.rb, line 313 def request_phase raise NotImplementedError end
# File lib/omniauth/strategy.rb, line 423 def script_name @env['SCRIPT_NAME'] || '' end
# File lib/omniauth/strategy.rb, line 427 def session @env['rack.session'] end
# File lib/omniauth/strategy.rb, line 388 def setup_path options[:setup_path] || "#{path_prefix}/#{name}/setup" end
The setup phase looks for the `:setup` option to exist and, if it is, will call either the Rack endpoint supplied to the `:setup` option or it will call out to the setup path of the underlying application. This will default to `/auth/:provider/setup`.
# File lib/omniauth/strategy.rb, line 299 def setup_phase if options[:setup].respond_to?(:call) log :info, "Setup endpoint detected, running now." options[:setup].call(env) elsif options.setup? log :info, "Calling through to underlying application for setup." setup_env = env.merge('PATH_INFO' => setup_path, 'REQUEST_METHOD' => 'GET') call_app!(setup_env) end end
Determines whether or not user info should be retrieved. This allows some strategies to save a call to an external API service for existing users. You can use it either by setting the `:skip_info` to true or by setting `:skip_info` to a Proc that takes a uid and evaluates to true when you would like to skip info.
@example
use MyStrategy, :skip_info => lambda{|uid| User.find_by_uid(uid)}
# File lib/omniauth/strategy.rb, line 350 def skip_info? if options.skip_info? if options.skip_info.respond_to?(:call) return options.skip_info.call(uid) else return true end end false end
# File lib/omniauth/strategy.rb, line 317 def uid self.class.uid_stack(self).last end
# File lib/omniauth/strategy.rb, line 452 def user_info; {} end
# File lib/omniauth/strategy.rb, line 472 def merge_stack(stack) stack.inject({}){|c,h| c.merge!(h); c} end
# File lib/omniauth/strategy.rb, line 475 def ssl? request.env['HTTPS'] == 'on' || request.env['HTTP_X_FORWARDED_SSL'] == 'on' || request.env['HTTP_X_FORWARDED_SCHEME'] == 'https' || (request.env['HTTP_X_FORWARDED_PROTO'] && request.env['HTTP_X_FORWARDED_PROTO'].split(',')[0] == 'https') || request.env['rack.url_scheme'] == 'https' end