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 attr_reader :app, :env, :options, :response 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 124 def initialize(app, *args, &block) @app = app @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 326 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 156 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 165 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 393 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 212 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 377 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 354 def callback_phase self.env['omniauth.auth'] = auth_hash call_app! end
# File lib/omniauth/strategy.rb, line 413 def callback_url full_host + script_name + callback_path + query_string end
# File lib/omniauth/strategy.rb, line 318 def credentials merge_stack(self.class.credentials_stack(self)) end
# File lib/omniauth/strategy.rb, line 385 def current_path request.path_info.downcase.sub(%r\/$/,'') end
# File lib/omniauth/strategy.rb, line 363 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 322 def extra merge_stack(self.class.extra_stack(self)) end
# File lib/omniauth/strategy.rb, line 448 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 397 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(%r\?.*$/,'')) uri.path = '' uri.query = nil #sometimes the url is actually showing http inside rails because the other layers (like nginx) have handled the ssl termination. uri.scheme = 'https' if(request.env['HTTP_X_FORWARDED_PROTO'] == 'https') uri.to_s end end
# File lib/omniauth/strategy.rb, line 314 def info merge_stack(self.class.info_stack(self)) end
# File lib/omniauth/strategy.rb, line 141 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 150 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 255 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 274 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 261 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(%r#{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 429 def name options.name end
Returns true if the environment recognizes either the request or callback path.
# File lib/omniauth/strategy.rb, line 224 def on_auth_path? on_request_path? || on_callback_path? end
# File lib/omniauth/strategy.rb, line 236 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 244 def on_path?(path) current_path.casecmp(path) == 0 end
# File lib/omniauth/strategy.rb, line 228 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 181 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 248 def options_request? request.request_method == 'OPTIONS' end
# File lib/omniauth/strategy.rb, line 359 def path_prefix options[:path_prefix] || OmniAuth.config.path_prefix end
# File lib/omniauth/strategy.rb, line 389 def query_string request.query_string.empty? ? "" : "?#{request.query_string}" end
# File lib/omniauth/strategy.rb, line 433 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 425 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 187 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(%r#{request_path}$/) env['rack.session']['omniauth.origin'] = env['HTTP_REFERER'] end request_phase end end
# File lib/omniauth/strategy.rb, line 373 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 306 def request_phase raise NotImplementedError end
# File lib/omniauth/strategy.rb, line 417 def script_name @env['SCRIPT_NAME'] || '' end
# File lib/omniauth/strategy.rb, line 421 def session @env['rack.session'] end
# File lib/omniauth/strategy.rb, line 381 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 292 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 343 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 310 def uid self.class.uid_stack(self).last end
# File lib/omniauth/strategy.rb, line 446 def user_info; {} end
# File lib/omniauth/strategy.rb, line 466 def merge_stack(stack) stack.inject({}){|c,h| c.merge!(h); c} end