Module: OmniAuth::Strategy

Included in:
OmniAuth::Strategies::Developer
Defined in:
lib/omniauth/strategy.rb

Overview

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.

Defined Under Namespace

Modules: ClassMethods Classes: Options

Constant Summary collapse

CURRENT_PATH_REGEX =
%r{/$}
EMPTY_STRING =
''.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#appObject (readonly)

Returns the value of attribute app


112
113
114
# File 'lib/omniauth/strategy.rb', line 112

def app
  @app
end

#envObject (readonly)

Returns the value of attribute env


112
113
114
# File 'lib/omniauth/strategy.rb', line 112

def env
  @env
end

#optionsObject (readonly)

Returns the value of attribute options


112
113
114
# File 'lib/omniauth/strategy.rb', line 112

def options
  @options
end

#responseObject (readonly)

Returns the value of attribute response


112
113
114
# File 'lib/omniauth/strategy.rb', line 112

def response
  @response
end

Class Method Details

.included(base) ⇒ Object


10
11
12
13
14
15
16
17
18
# File 'lib/omniauth/strategy.rb', line 10

def self.included(base)
  OmniAuth.strategies << base

  base.extend ClassMethods
  base.class_eval do
    option :setup, false
    option :skip_info, false
  end
end

Instance Method Details

#auth_hashObject


332
333
334
335
336
337
338
# File 'lib/omniauth/strategy.rb', line 332

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

#call(env) ⇒ Object

Duplicates this instance and runs #call! on it.

Parameters:

  • The (Hash)

    Rack environment.


163
164
165
# File 'lib/omniauth/strategy.rb', line 163

def call(env)
  dup.call!(env)
end

#call!(env) ⇒ Object

The logic for dispatching any additional actions that need to be taken. For instance, calling the request phase if the request path is recognized.

Parameters:

  • env (Hash)

    The Rack environment.


172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/omniauth/strategy.rb', line 172

def call!(env) # rubocop:disable CyclomaticComplexity, PerceivedComplexity
  unless env['rack.session']
    error = OmniAuth::NoSessionError.new('You must provide a session to use OmniAuth.')
    raise(error)
  end

  @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

#call_app!(env = @env) ⇒ Object


407
408
409
# File 'lib/omniauth/strategy.rb', line 407

def call_app!(env = @env)
  @app.call(env)
end

#callback_callObject

Performs the steps necessary to run the callback phase of a strategy.


220
221
222
223
224
225
226
227
228
# File 'lib/omniauth/strategy.rb', line 220

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') || {}
  OmniAuth.config.before_callback_phase.call(@env) if OmniAuth.config.before_callback_phase
  callback_phase
end

#callback_pathObject


383
384
385
386
387
388
389
390
391
# File 'lib/omniauth/strategy.rb', line 383

def callback_path
  @callback_path ||= begin
    path = options[:callback_path] if options[:callback_path].is_a?(String)
    path ||= current_path if options[:callback_path].respond_to?(:call) && options[:callback_path].call(env)
    path ||= custom_path(:request_path)
    path ||= "#{path_prefix}/#{name}/callback"
    path
  end
end

#callback_phaseObject


360
361
362
363
# File 'lib/omniauth/strategy.rb', line 360

def callback_phase
  env['omniauth.auth'] = auth_hash
  call_app!
end

#callback_urlObject


431
432
433
# File 'lib/omniauth/strategy.rb', line 431

def callback_url
  full_host + script_name + callback_path + query_string
end

#credentialsObject


324
325
326
# File 'lib/omniauth/strategy.rb', line 324

def credentials
  merge_stack(self.class.credentials_stack(self))
end

#current_pathObject


399
400
401
# File 'lib/omniauth/strategy.rb', line 399

def current_path
  @current_path ||= request.path_info.downcase.sub(CURRENT_PATH_REGEX, EMPTY_STRING)
end

#custom_path(kind) ⇒ Object


369
370
371
372
373
374
375
376
377
# File 'lib/omniauth/strategy.rb', line 369

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

#dupObject


482
483
484
485
486
# File 'lib/omniauth/strategy.rb', line 482

def dup
  super.tap do
    @options = @options.dup
  end
end

#extraObject


328
329
330
# File 'lib/omniauth/strategy.rb', line 328

def extra
  merge_stack(self.class.extra_stack(self))
end

#fail!(message_key, exception = nil) ⇒ Object


468
469
470
471
472
473
474
475
476
477
478
479
480
# File 'lib/omniauth/strategy.rb', line 468

def fail!(message_key, exception = nil)
  env['omniauth.error'] = exception
  env['omniauth.error.type'] = message_key.to_sym
  env['omniauth.error.strategy'] = self

  if exception
    log :error, "Authentication failure! #{message_key}: #{exception.class}, #{exception.message}"
  else
    log :error, "Authentication failure! #{message_key} encountered."
  end

  OmniAuth.config.on_failure.call(env)
end

#full_hostObject


411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# File 'lib/omniauth/strategy.rb', line 411

def full_host
  case OmniAuth.config.full_host
  when String
    OmniAuth.config.full_host
  when Proc
    OmniAuth.config.full_host.call(env)
  else
    # in Rack 1.3.x, request.url explodes if scheme is nil
    if request.scheme && request.url.match(URI::ABS_URI)
      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? # rubocop:disable BlockNesting
      uri.to_s
    else ''
    end
  end
end

#infoObject


320
321
322
# File 'lib/omniauth/strategy.rb', line 320

def info
  merge_stack(self.class.info_stack(self))
end

#new(app, options = {}) ⇒ Object #new(app, *args, options = {}) ⇒ Object

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.

Overloads:

  • #new(app, options = {}) ⇒ Object

    If nothing but a hash is supplied, initialized with the supplied options overriding the strategy's default options via a deep merge.

  • #new(app, *args, options = {}) ⇒ Object

    If the strategy has supplied custom arguments that it accepts, they may will be passed through and set to the appropriate values.

Parameters:

  • app (Rack application)

    The application on which this middleware is applied.

Yields:

  • (Options)

    Yields options to block for further configuration.

Raises:

  • (ArgumentError)

129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/omniauth/strategy.rb', line 129

def initialize(app, *args, &block) # rubocop:disable UnusedMethodArgument
  @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|
    break if args.empty?
    options[arg] = args.shift
  end

  # Make sure that all of the args have been dealt with, otherwise error out.
  raise(ArgumentError.new("Received wrong number of arguments. #{args.inspect}")) unless args.empty?

  yield options if block_given?
end

#inspectObject


148
149
150
# File 'lib/omniauth/strategy.rb', line 148

def inspect
  "#<#{self.class}>"
end

#log(level, message) ⇒ Object

Direct access to the OmniAuth logger, automatically prefixed with this strategy's name.

Examples:

log :warn, "This is a warning."

157
158
159
# File 'lib/omniauth/strategy.rb', line 157

def log(level, message)
  OmniAuth.logger.send(level, "(#{name}) #{message}")
end

#mock_call!Object

This is called in lieu of the normal request process in the event that OmniAuth has been configured to be in test mode.


259
260
261
262
263
# File 'lib/omniauth/strategy.rb', line 259

def mock_call!(*)
  return mock_request_call if on_request_path?
  return mock_callback_call if on_callback_path?
  call_app!
end

#mock_callback_callObject


279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/omniauth/strategy.rb', line 279

def mock_callback_call
  setup_phase
  @env['omniauth.origin'] = session.delete('omniauth.origin')
  @env['omniauth.origin'] = nil if env['omniauth.origin'] == ''
  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') || {}
    OmniAuth.config.before_callback_phase.call(@env) if OmniAuth.config.before_callback_phase
    call_app!
  end
end

#mock_request_callObject


265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/omniauth/strategy.rb', line 265

def mock_request_call
  setup_phase

  session['omniauth.params'] = request.params
  OmniAuth.config.before_request_phase.call(env) if OmniAuth.config.before_request_phase
  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(callback_url)
end

#nameObject


447
448
449
# File 'lib/omniauth/strategy.rb', line 447

def name
  options.name
end

#on_auth_path?Boolean

Returns true if the environment recognizes either the request or callback path.

Returns:

  • (Boolean)

232
233
234
# File 'lib/omniauth/strategy.rb', line 232

def on_auth_path?
  on_request_path? || on_callback_path?
end

#on_callback_path?Boolean

Returns:

  • (Boolean)

244
245
246
# File 'lib/omniauth/strategy.rb', line 244

def on_callback_path?
  on_path?(callback_path)
end

#on_path?(path) ⇒ Boolean

Returns:

  • (Boolean)

248
249
250
# File 'lib/omniauth/strategy.rb', line 248

def on_path?(path)
  current_path.casecmp(path).zero?
end

#on_request_path?Boolean

Returns:

  • (Boolean)

236
237
238
239
240
241
242
# File 'lib/omniauth/strategy.rb', line 236

def on_request_path?
  if options.request_path.respond_to?(:call)
    options.request_path.call(env)
  else
    on_path?(request_path)
  end
end

#options_callObject

Responds to an OPTIONS request.


190
191
192
193
194
# File 'lib/omniauth/strategy.rb', line 190

def options_call
  OmniAuth.config.before_options_phase.call(env) if OmniAuth.config.before_options_phase
  verbs = OmniAuth.config.allowed_request_methods.collect(&:to_s).collect(&:upcase).join(', ')
  [200, {'Allow' => verbs}, []]
end

#options_request?Boolean

Returns:

  • (Boolean)

252
253
254
# File 'lib/omniauth/strategy.rb', line 252

def options_request?
  request.request_method == 'OPTIONS'
end

#path_prefixObject


365
366
367
# File 'lib/omniauth/strategy.rb', line 365

def path_prefix
  options[:path_prefix] || OmniAuth.config.path_prefix
end

#query_stringObject


403
404
405
# File 'lib/omniauth/strategy.rb', line 403

def query_string
  request.query_string.empty? ? '' : "?#{request.query_string}"
end

#redirect(uri) ⇒ Object


451
452
453
454
455
456
457
458
459
460
461
462
# File 'lib/omniauth/strategy.rb', line 451

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

#requestObject


443
444
445
# File 'lib/omniauth/strategy.rb', line 443

def request
  @request ||= Rack::Request.new(@env)
end

#request_callObject

Performs the steps necessary to run the request phase of a strategy.


197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/omniauth/strategy.rb', line 197

def request_call # rubocop:disable CyclomaticComplexity, MethodLength, PerceivedComplexity
  setup_phase
  log :info, 'Request phase initiated.'  # store query params from the request url, extracted in the callback_phase

  session['omniauth.params'] = request.params
  OmniAuth.config.before_request_phase.call(env) if OmniAuth.config.before_request_phase
  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

#request_pathObject


379
380
381
# File 'lib/omniauth/strategy.rb', line 379

def request_path
  @request_path ||= options[:request_path].is_a?(String) ? options[:request_path] : "#{path_prefix}/#{name}"
end

#request_phaseObject

This method is 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.

Raises:

  • (NotImplementedError)

312
313
314
# File 'lib/omniauth/strategy.rb', line 312

def request_phase
  raise(NotImplementedError)
end

#script_nameObject


435
436
437
# File 'lib/omniauth/strategy.rb', line 435

def script_name
  @env['SCRIPT_NAME'] || ''
end

#sessionObject


439
440
441
# File 'lib/omniauth/strategy.rb', line 439

def session
  @env['rack.session']
end

#setup_pathObject


393
394
395
# File 'lib/omniauth/strategy.rb', line 393

def setup_path
  options[:setup_path] || "#{path_prefix}/#{name}/setup"
end

#setup_phaseObject

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.


298
299
300
301
302
303
304
305
306
307
# File 'lib/omniauth/strategy.rb', line 298

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

#skip_info?Boolean

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.

Examples:


use MyStrategy, :skip_info => lambda{|uid| User.find_by_uid(uid)}

Returns:

  • (Boolean)

349
350
351
352
353
354
355
356
357
358
# File 'lib/omniauth/strategy.rb', line 349

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

#uidObject


316
317
318
# File 'lib/omniauth/strategy.rb', line 316

def uid
  self.class.uid_stack(self).last
end

#user_infoObject


464
465
466
# File 'lib/omniauth/strategy.rb', line 464

def 
  {}
end