1
0
Fork 0
mirror of https://github.com/omniauth/omniauth.git synced 2022-11-09 12:31:49 -05:00

Merge pull request #1021 from omniauth/2_0-indev

Release v2.0.0
This commit is contained in:
Bobby McDonald 2021-01-11 14:39:56 -05:00 committed by GitHub
commit 29c8216e0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 484 additions and 110 deletions

View file

@ -9,9 +9,9 @@ name: Ruby
on: on:
push: push:
branches: [ master ] branches: [ master, 2_0-indev ]
pull_request: pull_request:
branches: [ master ] branches: [ master, 2_0-indev ]
jobs: jobs:
test: test:
@ -54,6 +54,21 @@ jobs:
env: env:
JRUBY_OPTS: --debug JRUBY_OPTS: --debug
run: bundle exec rake run: bundle exec rake
frozen-string-compat:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6
bundler-cache: true
- name: Install dependencies
run: bundle install
- name: Run tests
env:
RUBYOPT: "--enable-frozen-string-literal"
run: bundle exec rake
coveralls: coveralls:
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
steps: steps:
@ -61,7 +76,7 @@ jobs:
- name: Set up Ruby - name: Set up Ruby
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
with: with:
ruby-version: 2.5 ruby-version: 2.6
bundler-cache: true bundler-cache: true
- name: Install dependencies - name: Install dependencies
run: bundle install run: bundle install

View file

@ -19,7 +19,8 @@ group :test do
gem 'rack', '>= 2.0.6', :platforms => %i[jruby_18 jruby_19 ruby_19 ruby_20 ruby_21] gem 'rack', '>= 2.0.6', :platforms => %i[jruby_18 jruby_19 ruby_19 ruby_20 ruby_21]
gem 'rack-test' gem 'rack-test'
gem 'rest-client', '~> 2.0.0', :platforms => [:jruby_18] gem 'rest-client', '~> 2.0.0', :platforms => [:jruby_18]
gem 'rspec', '~> 3.5.0' gem 'rspec', '~> 3.5'
gem 'rack-freeze'
gem 'rubocop', '>= 0.58.2', '< 0.69.0', :platforms => %i[ruby_20 ruby_21 ruby_22 ruby_23 ruby_24] gem 'rubocop', '>= 0.58.2', '< 0.69.0', :platforms => %i[ruby_20 ruby_21 ruby_22 ruby_23 ruby_24]
gem 'simplecov-lcov' gem 'simplecov-lcov'
gem 'tins', '~> 1.13', :platforms => %i[jruby_18 jruby_19 ruby_19] gem 'tins', '~> 1.13', :platforms => %i[jruby_18 jruby_19 ruby_19]

View file

@ -2,9 +2,8 @@
[![Gem Version](http://img.shields.io/gem/v/omniauth.svg)][gem] [![Gem Version](http://img.shields.io/gem/v/omniauth.svg)][gem]
[![Build Status](http://img.shields.io/travis/omniauth/omniauth.svg)][travis] [![Build Status](http://img.shields.io/travis/omniauth/omniauth.svg)][travis]
[![Code Climate](http://img.shields.io/codeclimate/github/omniauth/omniauth.svg)][codeclimate] [![Code Climate](https://api.codeclimate.com/v1/badges/ffd33970723587806744/maintainability)][codeclimate]
[![Coverage Status](http://img.shields.io/coveralls/omniauth/omniauth.svg)][coveralls] [![Coverage Status](http://img.shields.io/coveralls/omniauth/omniauth.svg)][coveralls]
[![Security](https://hakiri.io/github/omniauth/omniauth/master.svg)](https://hakiri.io/github/omniauth/omniauth/master)
[gem]: https://rubygems.org/gems/omniauth [gem]: https://rubygems.org/gems/omniauth
[travis]: http://travis-ci.org/omniauth/omniauth [travis]: http://travis-ci.org/omniauth/omniauth
@ -33,8 +32,8 @@ development and easily swap in other strategies later.
## Getting Started ## Getting Started
Each OmniAuth strategy is a Rack Middleware. That means that you can use Each OmniAuth strategy is a Rack Middleware. That means that you can use
it the same way that you use any other Rack middleware. For example, to it the same way that you use any other Rack middleware. For example, to
use the built-in Developer strategy in a Sinatra application I might do use the built-in Developer strategy in a Sinatra application you might
this: do this:
```ruby ```ruby
require 'sinatra' require 'sinatra'
@ -46,7 +45,7 @@ class MyApplication < Sinatra::Base
end end
``` ```
Because OmniAuth is built for *multi-provider* authentication, I may Because OmniAuth is built for *multi-provider* authentication, you may
want to leave room to run multiple strategies. For this, the built-in want to leave room to run multiple strategies. For this, the built-in
`OmniAuth::Builder` class gives you an easy way to specify multiple `OmniAuth::Builder` class gives you an easy way to specify multiple
strategies. Note that there is **no difference** between the following strategies. Note that there is **no difference** between the following
@ -83,14 +82,14 @@ environment of a request to `/auth/:provider/callback`. This hash
contains as much information about the user as OmniAuth was able to contains as much information about the user as OmniAuth was able to
glean from the utilized strategy. You should set up an endpoint in your glean from the utilized strategy. You should set up an endpoint in your
application that matches to the callback URL and then performs whatever application that matches to the callback URL and then performs whatever
steps are necessary for your application. For example, in a Rails app I steps are necessary for your application. For example, in a Rails app
would add a line in my `routes.rb` file like this: you would add a line in your `routes.rb` file like this:
```ruby ```ruby
post '/auth/:provider/callback', to: 'sessions#create' post '/auth/:provider/callback', to: 'sessions#create'
``` ```
And I might then have a `SessionsController` with code that looks And you might then have a `SessionsController` with code that looks
something like this: something like this:
```ruby ```ruby
@ -112,7 +111,7 @@ class SessionsController < ApplicationController
end end
``` ```
The `omniauth.auth` key in the environment hash gives me my The `omniauth.auth` key in the environment hash provides an
Authentication Hash which will contain information about the just Authentication Hash which will contain information about the just
authenticated user including a unique id, the strategy they just used authenticated user including a unique id, the strategy they just used
for authentication, and personal details such as name and email address for authentication, and personal details such as name and email address
@ -167,7 +166,7 @@ a `session_store.rb` initializer, add `use ActionDispatch::Session::CookieStore`
and have sessions functioning as normal. and have sessions functioning as normal.
To be clear: sessions may work, but your session options will be ignored To be clear: sessions may work, but your session options will be ignored
(i.e the session key will default to `_session_id`). Instead of the (i.e. the session key will default to `_session_id`). Instead of the
initializer, you'll have to set the relevant options somewhere initializer, you'll have to set the relevant options somewhere
before your middleware is built (like `application.rb`) and pass them to your before your middleware is built (like `application.rb`) and pass them to your
preferred middleware, like this: preferred middleware, like this:

View file

@ -15,6 +15,7 @@ module OmniAuth
autoload :Form, 'omniauth/form' autoload :Form, 'omniauth/form'
autoload :AuthHash, 'omniauth/auth_hash' autoload :AuthHash, 'omniauth/auth_hash'
autoload :FailureEndpoint, 'omniauth/failure_endpoint' autoload :FailureEndpoint, 'omniauth/failure_endpoint'
autoload :AuthenticityTokenProtection, 'omniauth/authenticity_token_protection'
def self.strategies def self.strategies
@strategies ||= [] @strategies ||= []
@ -29,20 +30,22 @@ module OmniAuth
logger logger
end end
def self.defaults def self.defaults # rubocop:disable MethodLength
@defaults ||= { @defaults ||= {
:camelizations => {}, :camelizations => {},
:path_prefix => '/auth', :path_prefix => '/auth',
:on_failure => OmniAuth::FailureEndpoint, :on_failure => OmniAuth::FailureEndpoint,
:failure_raise_out_environments => ['development'], :failure_raise_out_environments => ['development'],
:request_validation_phase => OmniAuth::AuthenticityTokenProtection,
:before_request_phase => nil, :before_request_phase => nil,
:before_callback_phase => nil, :before_callback_phase => nil,
:before_options_phase => nil, :before_options_phase => nil,
:form_css => Form::DEFAULT_CSS, :form_css => Form::DEFAULT_CSS,
:test_mode => false, :test_mode => false,
:logger => default_logger, :logger => default_logger,
:allowed_request_methods => %i[get post], :allowed_request_methods => %i[post],
:mock_auth => {:default => AuthHash.new('provider' => 'default', 'uid' => '1234', 'info' => {'name' => 'Example User'})} :mock_auth => {:default => AuthHash.new('provider' => 'default', 'uid' => '1234', 'info' => {'name' => 'Example User'})},
:silence_get_warning => false
} }
end end
@ -74,6 +77,14 @@ module OmniAuth
end end
end end
def request_validation_phase(&block)
if block_given?
@request_validation_phase = block
else
@request_validation_phase
end
end
def before_request_phase(&block) def before_request_phase(&block)
if block_given? if block_given?
@before_request_phase = block @before_request_phase = block
@ -111,8 +122,9 @@ module OmniAuth
camelizations[name.to_s] = camelized.to_s camelizations[name.to_s] = camelized.to_s
end end
attr_writer :on_failure, :before_callback_phase, :before_options_phase, :before_request_phase attr_writer :on_failure, :before_callback_phase, :before_options_phase, :before_request_phase, :request_validation_phase
attr_accessor :failure_raise_out_environments, :path_prefix, :allowed_request_methods, :form_css, :test_mode, :mock_auth, :full_host, :camelizations, :logger attr_accessor :failure_raise_out_environments, :path_prefix, :allowed_request_methods, :form_css,
:test_mode, :mock_auth, :full_host, :camelizations, :logger, :silence_get_warning
end end
def self.config def self.config
@ -159,7 +171,7 @@ module OmniAuth
if first_letter_in_uppercase if first_letter_in_uppercase
word.to_s.gsub(%r{/(.?)}) { '::' + Regexp.last_match[1].upcase }.gsub(/(^|_)(.)/) { Regexp.last_match[2].upcase } word.to_s.gsub(%r{/(.?)}) { '::' + Regexp.last_match[1].upcase }.gsub(/(^|_)(.)/) { Regexp.last_match[2].upcase }
else else
word.first + camelize(word)[1..-1] camelize(word).tap { |w| w[0] = w[0].downcase }
end end
end end
end end

View file

@ -0,0 +1,30 @@
require 'rack-protection'
module OmniAuth
class AuthenticityError < StandardError; end
class AuthenticityTokenProtection < Rack::Protection::AuthenticityToken
def initialize(options = {})
@options = default_options.merge(options)
end
def self.call(env)
new.call!(env)
end
def call!(env)
return if accepts?(env)
instrument env
react env
end
private
def deny(_env)
OmniAuth.logger.send(:warn, "Attack prevented by #{self.class}")
raise AuthenticityError.new(options[:message])
end
alias default_reaction deny
end
end

View file

@ -31,7 +31,7 @@ module OmniAuth
middleware = klass middleware = klass
else else
begin begin
middleware = OmniAuth::Strategies.const_get(OmniAuth::Utils.camelize(klass.to_s).to_s) middleware = OmniAuth::Strategies.const_get(OmniAuth::Utils.camelize(klass.to_s).to_s, false)
rescue NameError rescue NameError
raise(LoadError.new("Could not find matching strategy for #{klass.inspect}. You may need to install an additional gem (such as omniauth-#{klass}).")) raise(LoadError.new("Could not find matching strategy for #{klass.inspect}. You may need to install an additional gem (such as omniauth-#{klass})."))
end end

View file

@ -27,10 +27,19 @@ module OmniAuth
def redirect_to_failure def redirect_to_failure
message_key = env['omniauth.error.type'] message_key = env['omniauth.error.type']
new_path = "#{env['SCRIPT_NAME']}#{OmniAuth.config.path_prefix}/failure?message=#{message_key}#{origin_query_param}#{strategy_name_query_param}"
new_path = "#{env['SCRIPT_NAME']}#{strategy_path_prefix}/failure?message=#{Rack::Utils.escape(message_key)}#{origin_query_param}#{strategy_name_query_param}"
Rack::Response.new(['302 Moved'], 302, 'Location' => new_path).finish Rack::Response.new(['302 Moved'], 302, 'Location' => new_path).finish
end end
def strategy_path_prefix
if env['omniauth.error.strategy']
env['omniauth.error.strategy'].path_prefix
else
OmniAuth.config.path_prefix
end
end
def strategy_name_query_param def strategy_name_query_param
return '' unless env['omniauth.error.strategy'] return '' unless env['omniauth.error.strategy']

View file

@ -9,7 +9,7 @@ module OmniAuth
options[:header_info] ||= '' options[:header_info] ||= ''
self.options = options self.options = options
@html = '' @html = +'' # unary + string allows it to be mutable if strings are frozen
@with_custom_button = false @with_custom_button = false
@footer = nil @footer = nil
header(options[:title], options[:header_info]) header(options[:title], options[:header_info])

View file

@ -180,18 +180,44 @@ module OmniAuth
raise(error) raise(error)
end end
warn_if_using_get
@env = env @env = env
@env['omniauth.strategy'] = self if on_auth_path? @env['omniauth.strategy'] = self if on_auth_path?
return mock_call!(env) if OmniAuth.config.test_mode 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) begin
return callback_call if on_callback_path? return options_call if on_auth_path? && options_request?
return other_phase if respond_to?(:other_phase) 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)
rescue StandardError => e
return fail!(e.message, e)
end
@app.call(env) @app.call(env)
end end
def warn_if_using_get
return unless OmniAuth.config.allowed_request_methods.include?(:get)
return if OmniAuth.config.silence_get_warning
log :warn, <<-WARN
You are using GET as an allowed request method for OmniAuth. This may leave
you open to CSRF attacks. As of v2.0.0, OmniAuth by default allows only POST
to its own routes. You should review the following resources to guide your
mitigation:
https://github.com/omniauth/omniauth/wiki/Resolving-CVE-2015-9284
https://github.com/omniauth/omniauth/issues/960
https://nvd.nist.gov/vuln/detail/CVE-2015-9284
https://github.com/omniauth/omniauth/pull/809
You can ignore this warning by setting:
OmniAuth.config.silence_get_warning = true
WARN
end
# Responds to an OPTIONS request. # Responds to an OPTIONS request.
def options_call def options_call
OmniAuth.config.before_options_phase.call(env) if OmniAuth.config.before_options_phase OmniAuth.config.before_options_phase.call(env) if OmniAuth.config.before_options_phase
@ -202,17 +228,19 @@ module OmniAuth
# Performs the steps necessary to run the request phase of a strategy. # Performs the steps necessary to run the request phase of a strategy.
def request_call # rubocop:disable CyclomaticComplexity, MethodLength, PerceivedComplexity def request_call # rubocop:disable CyclomaticComplexity, MethodLength, PerceivedComplexity
setup_phase setup_phase
log :info, 'Request phase initiated.' log :debug, 'Request phase initiated.'
# store query params from the request url, extracted in the callback_phase # store query params from the request url, extracted in the callback_phase
session['omniauth.params'] = request.GET session['omniauth.params'] = request.GET
OmniAuth.config.request_validation_phase.call(env) if OmniAuth.config.request_validation_phase
OmniAuth.config.before_request_phase.call(env) if OmniAuth.config.before_request_phase OmniAuth.config.before_request_phase.call(env) if OmniAuth.config.before_request_phase
if options.form.respond_to?(:call) if options.form.respond_to?(:call)
log :info, 'Rendering form from supplied Rack endpoint.' log :debug, 'Rendering form from supplied Rack endpoint.'
options.form.call(env) options.form.call(env)
elsif options.form elsif options.form
log :info, 'Rendering form from underlying application.' log :debug, 'Rendering form from underlying application.'
call_app! call_app!
elsif !options.origin_param elsif !options.origin_param
request_phase request_phase
@ -225,12 +253,14 @@ module OmniAuth
request_phase request_phase
end end
rescue OmniAuth::AuthenticityError => e
fail!(:authenticity_error, e)
end end
# Performs the steps necessary to run the callback phase of a strategy. # Performs the steps necessary to run the callback phase of a strategy.
def callback_call def callback_call
setup_phase setup_phase
log :info, 'Callback phase initiated.' log :debug, 'Callback phase initiated.'
@env['omniauth.origin'] = session.delete('omniauth.origin') @env['omniauth.origin'] = session.delete('omniauth.origin')
@env['omniauth.origin'] = nil if env['omniauth.origin'] == '' @env['omniauth.origin'] = nil if env['omniauth.origin'] == ''
@env['omniauth.params'] = session.delete('omniauth.params') || {} @env['omniauth.params'] = session.delete('omniauth.params') || {}
@ -268,8 +298,13 @@ module OmniAuth
# in the event that OmniAuth has been configured to be # in the event that OmniAuth has been configured to be
# in test mode. # in test mode.
def mock_call!(*) def mock_call!(*)
return mock_request_call if on_request_path? && OmniAuth.config.allowed_request_methods.include?(request.request_method.downcase.to_sym) begin
return mock_callback_call if on_callback_path? OmniAuth.config.request_validation_phase.call(env) if OmniAuth.config.request_validation_phase
return mock_request_call if on_request_path? && OmniAuth.config.allowed_request_methods.include?(request.request_method.downcase.to_sym)
return mock_callback_call if on_callback_path?
rescue StandardError => e
return fail!(e.message, e)
end
call_app! call_app!
end end
@ -312,10 +347,10 @@ module OmniAuth
# underlying application. This will default to `/auth/:provider/setup`. # underlying application. This will default to `/auth/:provider/setup`.
def setup_phase def setup_phase
if options[:setup].respond_to?(:call) if options[:setup].respond_to?(:call)
log :info, 'Setup endpoint detected, running now.' log :debug, 'Setup endpoint detected, running now.'
options[:setup].call(env) options[:setup].call(env)
elsif options[:setup] elsif options[:setup]
log :info, 'Calling through to underlying application for setup.' log :debug, 'Calling through to underlying application for setup.'
setup_env = env.merge('PATH_INFO' => setup_path, 'REQUEST_METHOD' => 'GET') setup_env = env.merge('PATH_INFO' => setup_path, 'REQUEST_METHOD' => 'GET')
call_app!(setup_env) call_app!(setup_env)
end end
@ -345,11 +380,13 @@ module OmniAuth
end end
def auth_hash def auth_hash
hash = AuthHash.new(:provider => name, :uid => uid) credentials_data = credentials
hash.info = info unless skip_info? extra_data = extra
hash.credentials = credentials if credentials AuthHash.new(:provider => name, :uid => uid).tap do |auth|
hash.extra = extra if extra auth.info = info unless skip_info?
hash auth.credentials = credentials_data if credentials_data
auth.extra = extra_data if extra_data
end
end end
# Determines whether or not user info should be retrieved. This # Determines whether or not user info should be retrieved. This
@ -389,7 +426,12 @@ module OmniAuth
end end
def request_path def request_path
@request_path ||= options[:request_path].is_a?(String) ? options[:request_path] : "#{path_prefix}/#{name}" @request_path ||=
if options[:request_path].is_a?(String)
options[:request_path]
else
"#{script_name}#{path_prefix}/#{name}"
end
end end
def callback_path def callback_path
@ -397,7 +439,7 @@ module OmniAuth
path = options[:callback_path] if options[:callback_path].is_a?(String) 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 ||= current_path if options[:callback_path].respond_to?(:call) && options[:callback_path].call(env)
path ||= custom_path(:request_path) path ||= custom_path(:request_path)
path ||= "#{path_prefix}/#{name}/callback" path ||= "#{script_name}#{path_prefix}/#{name}/callback"
path path
end end
end end
@ -409,7 +451,7 @@ module OmniAuth
CURRENT_PATH_REGEX = %r{/$}.freeze CURRENT_PATH_REGEX = %r{/$}.freeze
EMPTY_STRING = ''.freeze EMPTY_STRING = ''.freeze
def current_path def current_path
@current_path ||= request.path_info.downcase.sub(CURRENT_PATH_REGEX, EMPTY_STRING) @current_path ||= request.path.downcase.sub(CURRENT_PATH_REGEX, EMPTY_STRING)
end end
def query_string def query_string
@ -441,7 +483,7 @@ module OmniAuth
end end
def callback_url def callback_url
full_host + script_name + callback_path + query_string full_host + callback_path + query_string
end end
def script_name def script_name
@ -491,16 +533,15 @@ module OmniAuth
OmniAuth.config.on_failure.call(env) OmniAuth.config.on_failure.call(env)
end end
def dup
super.tap do
@options = @options.dup
end
end
class Options < OmniAuth::KeyStore; end class Options < OmniAuth::KeyStore; end
protected protected
def initialize_copy(*args)
super
@options = @options.dup
end
def merge_stack(stack) def merge_stack(stack)
stack.inject({}) do |a, e| stack.inject({}) do |a, e|
a.merge!(e) a.merge!(e)

View file

@ -1,3 +1,3 @@
module OmniAuth module OmniAuth
VERSION = '1.9.1'.freeze VERSION = '2.0.0'.freeze
end end

View file

@ -8,6 +8,7 @@ Gem::Specification.new do |spec|
spec.add_dependency 'hashie', ['>= 3.4.6'] spec.add_dependency 'hashie', ['>= 3.4.6']
spec.add_dependency 'rack', ['>= 1.6.2', '< 3'] spec.add_dependency 'rack', ['>= 1.6.2', '< 3']
spec.add_development_dependency 'bundler', '~> 2.0' spec.add_development_dependency 'bundler', '~> 2.0'
spec.add_dependency 'rack-protection'
spec.add_development_dependency 'rake', '~> 12.0' spec.add_development_dependency 'rake', '~> 12.0'
spec.authors = ['Michael Bleigh', 'Erik Michaels-Ober', 'Tom Milewski'] spec.authors = ['Michael Bleigh', 'Erik Michaels-Ober', 'Tom Milewski']
spec.description = 'A generalized Rack framework for multiple-provider authentication.' spec.description = 'A generalized Rack framework for multiple-provider authentication.'

View file

@ -20,10 +20,12 @@ end
require 'rspec' require 'rspec'
require 'rack/test' require 'rack/test'
require 'rack/freeze'
require 'omniauth' require 'omniauth'
require 'omniauth/test' require 'omniauth/test'
OmniAuth.config.logger = Logger.new('/dev/null') OmniAuth.config.logger = Logger.new('/dev/null')
OmniAuth.config.request_validation_phase = nil
RSpec.configure do |config| RSpec.configure do |config|
config.include Rack::Test::Methods config.include Rack::Test::Methods
@ -49,7 +51,7 @@ class ExampleStrategy
def request_phase def request_phase
options[:mutate_on_request].call(options) if options[:mutate_on_request] options[:mutate_on_request].call(options) if options[:mutate_on_request]
@fail = fail!(options[:failure]) if options[:failure] @fail = fail!(options[:failure], options[:failure_exception]) if options[:failure]
@last_env = env @last_env = env
return @fail if @fail return @fail if @fail
@ -58,7 +60,7 @@ class ExampleStrategy
def callback_phase def callback_phase
options[:mutate_on_callback].call(options) if options[:mutate_on_callback] options[:mutate_on_callback].call(options) if options[:mutate_on_callback]
@fail = fail!(options[:failure]) if options[:failure] @fail = fail!(options[:failure], options[:failure_exception]) if options[:failure]
@last_env = env @last_env = env
return @fail if @fail return @fail if @fail

View file

@ -13,6 +13,10 @@ describe OmniAuth::AuthHash do
expect(subject.weird_field.info).to eq 'string' expect(subject.weird_field.info).to eq 'string'
end end
it 'has a subkey_class' do
expect(OmniAuth::AuthHash.subkey_class).to eq Hashie::Mash
end
describe '#valid?' do describe '#valid?' do
subject { OmniAuth::AuthHash.new(:uid => '123', :provider => 'example', :info => {:name => 'Steven'}) } subject { OmniAuth::AuthHash.new(:uid => '123', :provider => 'example', :info => {:name => 'Steven'}) }
@ -111,6 +115,10 @@ describe OmniAuth::AuthHash do
end end
end end
it 'has a subkey_class' do
expect(OmniAuth::AuthHash::InfoHash.subkey_class).to eq Hashie::Mash
end
require 'hashie/version' require 'hashie/version'
if Gem::Version.new(Hashie::VERSION) >= Gem::Version.new('3.5.1') if Gem::Version.new(Hashie::VERSION) >= Gem::Version.new('3.5.1')
context 'with Hashie 3.5.1+' do context 'with Hashie 3.5.1+' do

View file

@ -3,7 +3,7 @@ require 'helper'
describe OmniAuth::Builder do describe OmniAuth::Builder do
describe '#provider' do describe '#provider' do
it 'translates a symbol to a constant' do it 'translates a symbol to a constant' do
expect(OmniAuth::Strategies).to receive(:const_get).with('MyStrategy').and_return(Class.new) expect(OmniAuth::Strategies).to receive(:const_get).with('MyStrategy', false).and_return(Class.new)
OmniAuth::Builder.new(nil) do OmniAuth::Builder.new(nil) do
provider :my_strategy provider :my_strategy
end end
@ -26,6 +26,16 @@ describe OmniAuth::Builder do
end end
end.to raise_error(LoadError, 'Could not find matching strategy for :lorax. You may need to install an additional gem (such as omniauth-lorax).') end.to raise_error(LoadError, 'Could not find matching strategy for :lorax. You may need to install an additional gem (such as omniauth-lorax).')
end end
it "doesn't translate a symbol to a top-level constant" do
class MyStrategy; end
expect do
OmniAuth::Builder.new(nil) do
provider :my_strategy
end
end.to raise_error(LoadError, 'Could not find matching strategy for :my_strategy. You may need to install an additional gem (such as omniauth-my_strategy).')
end
end end
describe '#options' do describe '#options' do
@ -111,14 +121,11 @@ describe OmniAuth::Builder do
describe '#call' do describe '#call' do
it 'passes env to to_app.call' do it 'passes env to to_app.call' do
app = lambda { |_env| [200, {}, []] } app = lambda { |env| [200, {}, env['CUSTOM_ENV_VALUE']] }
builder = OmniAuth::Builder.new(app) builder = OmniAuth::Builder.new(app)
env = {'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/some/path'} env = {'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/some/path', 'CUSTOM_ENV_VALUE' => 'VALUE'}
allow(app).to receive(:call).and_call_original
builder.call(env) expect(builder.call(env)).to eq([200, {}, 'VALUE'])
expect(app).to have_received(:call).with(env)
end end
end end

View file

@ -43,16 +43,27 @@ describe OmniAuth::FailureEndpoint do
expect(head['Location']).to eq('/random/auth/failure?message=invalid_request&strategy=test') expect(head['Location']).to eq('/random/auth/failure?message=invalid_request&strategy=test')
end end
it 'respects the configured path prefix' do it 'respects the globally configured path prefix' do
allow(OmniAuth.config).to receive(:path_prefix).and_return('/boo') allow(OmniAuth.config).to receive(:path_prefix).and_return('/boo')
_, head, = *subject.call(env) _, head, = *subject.call(env)
expect(head['Location']).to eq('/boo/failure?message=invalid_request&strategy=test') expect(head['Location']).to eq('/boo/failure?message=invalid_request&strategy=test')
end end
it 'respects the custom path prefix configured on the strategy' do
env['omniauth.error.strategy'] = ExampleStrategy.new({}, path_prefix: "/some/custom/path")
_, head, = *subject.call(env)
expect(head['Location']).to eq('/some/custom/path/failure?message=invalid_request&strategy=test')
end
it 'includes the origin (escaped) if one is provided' do it 'includes the origin (escaped) if one is provided' do
env['omniauth.origin'] = '/origin-example' env['omniauth.origin'] = '/origin-example'
_, head, = *subject.call(env) _, head, = *subject.call(env)
expect(head['Location']).to be_include('&origin=%2Forigin-example') expect(head['Location']).to be_include('&origin=%2Forigin-example')
end end
it 'escapes the message key' do
_, head = *subject.call(env.merge('omniauth.error.type' => 'Connection refused!'))
expect(head['Location']).to be_include('message=Connection+refused%21')
end
end end
end end

View file

@ -7,7 +7,8 @@ describe OmniAuth::Form do
end end
it 'evaluates in the instance when called with a block and no argument' do it 'evaluates in the instance when called with a block and no argument' do
OmniAuth::Form.build { |f| expect(f.class).to eq(OmniAuth::Form) } f = OmniAuth::Form.build { @html = '<h1>OmniAuth</h1>' }
expect(f.instance_variable_get(:@html)).to eq('<h1>OmniAuth</h1>')
end end
end end
@ -20,4 +21,34 @@ describe OmniAuth::Form do
expect(OmniAuth::Form.new(:title => 'Something Cool').to_html).to be_include('<h1>Something Cool</h1>') expect(OmniAuth::Form.new(:title => 'Something Cool').to_html).to be_include('<h1>Something Cool</h1>')
end end
end end
describe '#password_field' do
it 'adds a labeled input field' do
form = OmniAuth::Form.new.password_field('pass', 'password')
form_html = form.to_html
expect(form_html).to include('<label for=\'password\'>pass:</label>')
expect(form_html).to include('<input type=\'password\' id=\'password\' name=\'password\'/>')
end
end
describe '#html' do
it 'appends to the html body' do
form = OmniAuth::Form.build { @html = +'<p></p>' }
form.html('<h1></h1>')
expect(form.instance_variable_get(:@html)).to eq '<p></p><h1></h1>'
end
end
describe 'fieldset' do
it 'creates a fieldset with options' do
form = OmniAuth::Form.new
options = {:style => 'color: red', :id => 'fieldSetId'}
expected = "<fieldset style='color: red' id='fieldSetId'>\n <legend>legendary</legend>\n\n</fieldset>"
form.fieldset('legendary', options) {}
expect(form.to_html).to include expected
end
end
end end

View file

@ -25,6 +25,7 @@ RSpec.describe OmniAuth::KeyStore do
it 'does not log anything to the console' do it 'does not log anything to the console' do
stub_const('Hashie::VERSION', version) stub_const('Hashie::VERSION', version)
allow(OmniAuth::KeyStore).to receive(:respond_to?).with(:disable_warnings).and_return(false)
OmniAuth::KeyStore.override_logging OmniAuth::KeyStore.override_logging
expect(logger).not_to receive(:info) expect(logger).not_to receive(:info)
OmniAuth::KeyStore.new(:id => 1234) OmniAuth::KeyStore.new(:id => 1234)

View file

@ -10,7 +10,7 @@ describe OmniAuth::Strategies::Developer do
end end
context 'request phase' do context 'request phase' do
before(:each) { get '/auth/developer' } before(:each) { post '/auth/developer' }
it 'displays a form' do it 'displays a form' do
expect(last_response.status).to eq(200) expect(last_response.status).to eq(200)

View file

@ -2,7 +2,7 @@ require 'helper'
def make_env(path = '/auth/test', props = {}) def make_env(path = '/auth/test', props = {})
{ {
'REQUEST_METHOD' => 'GET', 'REQUEST_METHOD' => 'POST',
'PATH_INFO' => path, 'PATH_INFO' => path,
'rack.session' => {}, 'rack.session' => {},
'rack.input' => StringIO.new('test=true') 'rack.input' => StringIO.new('test=true')
@ -32,6 +32,12 @@ describe OmniAuth::Strategy do
end end
end end
describe 'user_info' do
it 'should default to an empty hash' do
expect(fresh_strategy.new(app, :skip_info => true).user_info).to eq({})
end
end
describe '.configure' do describe '.configure' do
subject do subject do
c = Class.new c = Class.new
@ -63,6 +69,29 @@ describe OmniAuth::Strategy do
end end
end end
describe '#fail!' do
it 'provides exception information when one is provided' do
env = make_env
exception = RuntimeError.new('No session!')
expect(OmniAuth.logger).to receive(:error).with(
"(test) Authentication failure! failed: #{exception.class}, #{exception.message}"
)
ExampleStrategy.new(app, :failure => :failed, :failure_exception => exception).call(env)
end
it 'provides a generic message when not provided an exception' do
env = make_env
expect(OmniAuth.logger).to receive(:error).with(
'(test) Authentication failure! Some Issue encountered.'
)
ExampleStrategy.new(app, :failure => 'Some Issue').call(env)
end
end
describe '#skip_info?' do describe '#skip_info?' do
it 'is true if options.skip_info is true' do it 'is true if options.skip_info is true' do
expect(ExampleStrategy.new(app, :skip_info => true)).to be_skip_info expect(ExampleStrategy.new(app, :skip_info => true)).to be_skip_info
@ -173,19 +202,25 @@ describe OmniAuth::Strategy do
end end
let(:instance) { subject.new(app) } let(:instance) { subject.new(app) }
it 'calls through to uid and info' do it 'calls through to uid, info, credentials, and extra' do
expect(instance).to receive(:uid) expect(instance).to receive(:uid)
expect(instance).to receive(:info) expect(instance).to receive(:info)
expect(instance).to receive(:credentials).and_return(expires: true).once
expect(instance).to receive(:extra).and_return(something: 'else').once
instance.auth_hash instance.auth_hash
end end
it 'returns an AuthHash' do it 'returns an AuthHash' do
allow(instance).to receive(:uid).and_return('123') allow(instance).to receive(:uid).and_return('123')
allow(instance).to receive(:info).and_return(:name => 'Hal Awesome') allow(instance).to receive(:info).and_return(:name => 'Hal Awesome')
allow(instance).to receive(:credentials).and_return(expires: true)
allow(instance).to receive(:extra).and_return(something: 'else')
hash = instance.auth_hash hash = instance.auth_hash
expect(hash).to be_kind_of(OmniAuth::AuthHash) expect(hash).to be_kind_of(OmniAuth::AuthHash)
expect(hash.uid).to eq('123') expect(hash.uid).to eq('123')
expect(hash.info.name).to eq('Hal Awesome') expect(hash.info.name).to eq('Hal Awesome')
expect(hash.credentials.expires).to eq(true)
expect(hash.extra.something).to eq('else')
end end
end end
@ -312,7 +347,9 @@ describe OmniAuth::Strategy do
context 'disabled' do context 'disabled' do
it 'does not set omniauth.origin' do it 'does not set omniauth.origin' do
@options = {:origin_param => false} @options = {:origin_param => false}
expect { strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'return=/foo')) }.to raise_error('Request Phase') expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'return=/foo'))
expect(strategy.last_env['rack.session']['omniauth.origin']).to eq(nil) expect(strategy.last_env['rack.session']['omniauth.origin']).to eq(nil)
end end
end end
@ -320,24 +357,31 @@ describe OmniAuth::Strategy do
context 'custom' do context 'custom' do
it 'sets from a custom param' do it 'sets from a custom param' do
@options = {:origin_param => 'return'} @options = {:origin_param => 'return'}
expect { strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'return=/foo')) }.to raise_error('Request Phase') expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'return=/foo'))
expect(strategy.last_env['rack.session']['omniauth.origin']).to eq('/foo') expect(strategy.last_env['rack.session']['omniauth.origin']).to eq('/foo')
end end
end end
context 'default flow' do context 'default flow' do
it 'is set on the request phase' do it 'is set on the request phase' do
expect { strategy.call(make_env('/auth/test', 'HTTP_REFERER' => 'http://example.com/origin')) }.to raise_error('Request Phase') expect(strategy).to receive(:fail!).with("Request Phase", kind_of(StandardError))
strategy.call(make_env('/auth/test', 'HTTP_REFERER' => 'http://example.com/origin'))
expect(strategy.last_env['rack.session']['omniauth.origin']).to eq('http://example.com/origin') expect(strategy.last_env['rack.session']['omniauth.origin']).to eq('http://example.com/origin')
end end
it 'is turned into an env variable on the callback phase' do it 'is turned into an env variable on the callback phase' do
expect { strategy.call(make_env('/auth/test/callback', 'rack.session' => {'omniauth.origin' => 'http://example.com/origin'})) }.to raise_error('Callback Phase') expect(strategy).to receive(:fail!).with("Callback Phase", kind_of(StandardError))
strategy.call(make_env('/auth/test/callback', 'rack.session' => {'omniauth.origin' => 'http://example.com/origin'}))
expect(strategy.last_env['omniauth.origin']).to eq('http://example.com/origin') expect(strategy.last_env['omniauth.origin']).to eq('http://example.com/origin')
end end
it 'sets from the params if provided' do it 'sets from the params if provided' do
expect { strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'origin=/foo')) }.to raise_error('Request Phase') expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'origin=/foo'))
expect(strategy.last_env['rack.session']['omniauth.origin']).to eq('/foo') expect(strategy.last_env['rack.session']['omniauth.origin']).to eq('/foo')
end end
@ -350,7 +394,9 @@ describe OmniAuth::Strategy do
context 'with script_name' do context 'with script_name' do
it 'is set on the request phase, containing full path' do it 'is set on the request phase, containing full path' do
env = {'HTTP_REFERER' => 'http://example.com/sub_uri/origin', 'SCRIPT_NAME' => '/sub_uri'} env = {'HTTP_REFERER' => 'http://example.com/sub_uri/origin', 'SCRIPT_NAME' => '/sub_uri'}
expect { strategy.call(make_env('/auth/test', env)) }.to raise_error('Request Phase') expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
strategy.call(make_env('/auth/test', env))
expect(strategy.last_env['rack.session']['omniauth.origin']).to eq('http://example.com/sub_uri/origin') expect(strategy.last_env['rack.session']['omniauth.origin']).to eq('http://example.com/sub_uri/origin')
end end
@ -359,8 +405,9 @@ describe OmniAuth::Strategy do
'rack.session' => {'omniauth.origin' => 'http://example.com/sub_uri/origin'}, 'rack.session' => {'omniauth.origin' => 'http://example.com/sub_uri/origin'},
'SCRIPT_NAME' => '/sub_uri' 'SCRIPT_NAME' => '/sub_uri'
} }
expect(strategy).to receive(:fail!).with('Callback Phase', kind_of(StandardError))
expect { strategy.call(make_env('/auth/test/callback', env)) }.to raise_error('Callback Phase') strategy.call(make_env('/auth/test/callback', env))
expect(strategy.last_env['omniauth.origin']).to eq('http://example.com/sub_uri/origin') expect(strategy.last_env['omniauth.origin']).to eq('http://example.com/sub_uri/origin')
end end
end end
@ -369,34 +416,41 @@ describe OmniAuth::Strategy do
context 'default paths' do context 'default paths' do
it 'uses the default request path' do it 'uses the default request path' do
expect { strategy.call(make_env) }.to raise_error('Request Phase') expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
strategy.call(make_env)
end end
it 'is case insensitive on request path' do it 'is case insensitive on request path' do
expect { strategy.call(make_env('/AUTH/Test')) }.to raise_error('Request Phase') expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
strategy.call(make_env('/AUTH/Test'))
end end
it 'is case insensitive on callback path' do it 'is case insensitive on callback path' do
expect { strategy.call(make_env('/AUTH/TeSt/CaLlBAck')) }.to raise_error('Callback Phase') expect(strategy).to receive(:fail!).with('Callback Phase', kind_of(StandardError))
strategy.call(make_env('/AUTH/TeSt/CaLlBAck'))
end end
it 'uses the default callback path' do it 'uses the default callback path' do
expect { strategy.call(make_env('/auth/test/callback')) }.to raise_error('Callback Phase') expect(strategy).to receive(:fail!).with('Callback Phase', kind_of(StandardError))
strategy.call(make_env('/auth/test/callback'))
end end
it 'strips trailing spaces on request' do it 'strips trailing spaces on request' do
expect { strategy.call(make_env('/auth/test/')) }.to raise_error('Request Phase') expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
strategy.call(make_env('/auth/test/'))
end end
it 'strips trailing spaces on callback' do it 'strips trailing spaces on callback' do
expect { strategy.call(make_env('/auth/test/callback/')) }.to raise_error('Callback Phase') expect(strategy).to receive(:fail!).with('Callback Phase', kind_of(StandardError))
strategy.call(make_env('/auth/test/callback/'))
end end
context 'callback_url' do context 'callback_url' do
it 'uses the default callback_path' do it 'uses the default callback_path' do
expect(strategy).to receive(:full_host).and_return('http://example.com') expect(strategy).to receive(:full_host).and_return('http://example.com')
expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
expect { strategy.call(make_env) }.to raise_error('Request Phase') strategy.call(make_env)
expect(strategy.callback_url).to eq('http://example.com/auth/test/callback') expect(strategy.callback_url).to eq('http://example.com/auth/test/callback')
end end
@ -436,12 +490,15 @@ describe OmniAuth::Strategy do
context 'dynamic paths' do context 'dynamic paths' do
it 'runs the request phase if the custom request path evaluator is truthy' do it 'runs the request phase if the custom request path evaluator is truthy' do
@options = {:request_path => lambda { |_env| true }} @options = {:request_path => lambda { |_env| true }}
expect { strategy.call(make_env('/asoufibasfi')) }.to raise_error('Request Phase') expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
strategy.call(make_env('/asoufibasfi'))
end end
it 'runs the callback phase if the custom callback path evaluator is truthy' do it 'runs the callback phase if the custom callback path evaluator is truthy' do
@options = {:callback_path => lambda { |_env| true }} @options = {:callback_path => lambda { |_env| true }}
expect { strategy.call(make_env('/asoufiasod')) }.to raise_error('Callback Phase') expect(strategy).to receive(:fail!).with('Callback Phase', kind_of(StandardError))
strategy.call(make_env('/asoufiasod'))
end end
it 'provides a custom callback path if request_path evals to a string' do it 'provides a custom callback path if request_path evals to a string' do
@ -451,8 +508,9 @@ describe OmniAuth::Strategy do
it 'correctly reports the callback path when the custom callback path evaluator is truthy' do it 'correctly reports the callback path when the custom callback path evaluator is truthy' do
strategy_instance = ExampleStrategy.new(app, :callback_path => lambda { |env| env['PATH_INFO'] == '/auth/bish/bosh/callback' }) strategy_instance = ExampleStrategy.new(app, :callback_path => lambda { |env| env['PATH_INFO'] == '/auth/bish/bosh/callback' })
expect(strategy_instance).to receive(:fail!).with('Callback Phase', kind_of(StandardError))
expect { strategy_instance.call(make_env('/auth/bish/bosh/callback')) }.to raise_error('Callback Phase') strategy_instance.call(make_env('/auth/bish/bosh/callback'))
expect(strategy_instance.callback_path).to eq('/auth/bish/bosh/callback') expect(strategy_instance.callback_path).to eq('/auth/bish/bosh/callback')
end end
end end
@ -460,20 +518,25 @@ describe OmniAuth::Strategy do
context 'custom paths' do context 'custom paths' do
it 'uses a custom request_path if one is provided' do it 'uses a custom request_path if one is provided' do
@options = {:request_path => '/awesome'} @options = {:request_path => '/awesome'}
expect { strategy.call(make_env('/awesome')) }.to raise_error('Request Phase') expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
strategy.call(make_env('/awesome'))
end end
it 'uses a custom callback_path if one is provided' do it 'uses a custom callback_path if one is provided' do
@options = {:callback_path => '/radical'} @options = {:callback_path => '/radical'}
expect { strategy.call(make_env('/radical')) }.to raise_error('Callback Phase') expect(strategy).to receive(:fail!).with('Callback Phase', kind_of(StandardError))
strategy.call(make_env('/radical'))
end end
context 'callback_url' do context 'callback_url' do
it 'uses a custom callback_path if one is provided' do it 'uses a custom callback_path if one is provided' do
@options = {:callback_path => '/radical'} @options = {:callback_path => '/radical'}
expect(strategy).to receive(:full_host).and_return('http://example.com') expect(strategy).to receive(:full_host).and_return('http://example.com')
expect(strategy).to receive(:fail!).with('Callback Phase', kind_of(StandardError))
expect { strategy.call(make_env('/radical')) }.to raise_error('Callback Phase') strategy.call(make_env('/radical'))
expect(strategy.callback_url).to eq('http://example.com/radical') expect(strategy.callback_url).to eq('http://example.com/radical')
end end
@ -496,18 +559,20 @@ describe OmniAuth::Strategy do
end end
it 'uses a custom prefix for request' do it 'uses a custom prefix for request' do
expect { strategy.call(make_env('/wowzers/test')) }.to raise_error('Request Phase') expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
strategy.call(make_env('/wowzers/test'))
end end
it 'uses a custom prefix for callback' do it 'uses a custom prefix for callback' do
expect { strategy.call(make_env('/wowzers/test/callback')) }.to raise_error('Callback Phase') expect(strategy).to receive(:fail!).with('Callback Phase', kind_of(StandardError))
strategy.call(make_env('/wowzers/test/callback'))
end end
context 'callback_url' do context 'callback_url' do
it 'uses a custom prefix' do it 'uses a custom prefix' do
expect(strategy).to receive(:full_host).and_return('http://example.com') expect(strategy).to receive(:full_host).and_return('http://example.com')
expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
expect { strategy.call(make_env('/wowzers/test')) }.to raise_error('Request Phase') strategy.call(make_env('/wowzers/test'))
expect(strategy.callback_url).to eq('http://example.com/wowzers/test/callback') expect(strategy.callback_url).to eq('http://example.com/wowzers/test/callback')
end end
@ -523,21 +588,66 @@ describe OmniAuth::Strategy do
end end
end end
context 'with relative url root' do
let(:props) { {'SCRIPT_NAME' => '/myapp'} }
it 'accepts the request' do
expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
strategy.call(make_env('/auth/test', props))
expect(strategy.request_path).to eq('/myapp/auth/test')
end
it 'accepts the callback' do
expect(strategy).to receive(:fail!).with('Callback Phase', kind_of(StandardError))
strategy.call(make_env('/auth/test/callback', props))
end
context 'callback_url' do
it 'redirects to the correctly prefixed callback' do
expect(strategy).to receive(:full_host).and_return('http://example.com')
expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
strategy.call(make_env('/auth/test', props))
expect(strategy.callback_url).to eq('http://example.com/myapp/auth/test/callback')
end
end
context 'custom request' do
before do
@options = {:request_path => '/myapp/override', :callback_path => '/myapp/override/callback'}
end
it 'does not prefix a custom request path' do
expect(strategy).to receive(:full_host).and_return('http://example.com')
expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
expect(strategy.request_path).to eq('/myapp/override')
strategy.call(make_env('/override', props))
expect(strategy.callback_url).to eq('http://example.com/myapp/override/callback')
end
end
end
context 'request method restriction' do context 'request method restriction' do
before do before(:context) do
OmniAuth.config.allowed_request_methods = [:post] OmniAuth.config.allowed_request_methods = %i[put post]
end end
it 'does not allow a request method of the wrong type' do it 'does not allow a request method of the wrong type' do
expect { strategy.call(make_env) }.not_to raise_error expect { strategy.call(make_env('/auth/test', 'REQUEST_METHOD' => 'GET')) }.not_to raise_error
end end
it 'allows a request method of the correct type' do it 'allows a request method of the correct type' do
expect { strategy.call(make_env('/auth/test', 'REQUEST_METHOD' => 'POST')) }.to raise_error('Request Phase') expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
strategy.call(make_env('/auth/test'))
end end
after do after(:context) do
OmniAuth.config.allowed_request_methods = %i[get post] OmniAuth.config.allowed_request_methods = %i[post]
end end
end end
@ -548,7 +658,7 @@ describe OmniAuth::Strategy do
end end
it 'sets the Allow header properly' do it 'sets the Allow header properly' do
expect(response[1]['Allow']).to eq('GET, POST') expect(response[1]['Allow']).to eq('POST')
end end
end end
@ -579,14 +689,16 @@ describe OmniAuth::Strategy do
it 'does not affect original options' do it 'does not affect original options' do
@options[:test_option] = true @options[:test_option] = true
@options[:mutate_on_request] = proc { |options| options.delete(:test_option) } @options[:mutate_on_request] = proc { |options| options.delete(:test_option) }
expect { strategy.call(make_env) }.to raise_error('Request Phase')
strategy.call(make_env)
expect(strategy.options).to have_key(:test_option) expect(strategy.options).to have_key(:test_option)
end end
it 'does not affect deep options' do it 'does not affect deep options' do
@options[:deep_option] = {:test_option => true} @options[:deep_option] = {:test_option => true}
@options[:mutate_on_request] = proc { |options| options[:deep_option].delete(:test_option) } @options[:mutate_on_request] = proc { |options| options[:deep_option].delete(:test_option) }
expect { strategy.call(make_env) }.to raise_error('Request Phase')
strategy.call(make_env)
expect(strategy.options[:deep_option]).to have_key(:test_option) expect(strategy.options[:deep_option]).to have_key(:test_option)
end end
end end
@ -595,14 +707,16 @@ describe OmniAuth::Strategy do
it 'does not affect original options' do it 'does not affect original options' do
@options[:test_option] = true @options[:test_option] = true
@options[:mutate_on_callback] = proc { |options| options.delete(:test_option) } @options[:mutate_on_callback] = proc { |options| options.delete(:test_option) }
expect { strategy.call(make_env('/auth/test/callback', 'REQUEST_METHOD' => 'POST')) }.to raise_error('Callback Phase')
strategy.call(make_env('/auth/test/callback', 'REQUEST_METHOD' => 'POST'))
expect(strategy.options).to have_key(:test_option) expect(strategy.options).to have_key(:test_option)
end end
it 'does not affect deep options' do it 'does not affect deep options' do
@options[:deep_option] = {:test_option => true} @options[:deep_option] = {:test_option => true}
@options[:mutate_on_callback] = proc { |options| options[:deep_option].delete(:test_option) } @options[:mutate_on_callback] = proc { |options| options[:deep_option].delete(:test_option) }
expect { strategy.call(make_env('/auth/test/callback', 'REQUEST_METHOD' => 'POST')) }.to raise_error('Callback Phase')
strategy.call(make_env('/auth/test/callback', 'REQUEST_METHOD' => 'POST'))
expect(strategy.options[:deep_option]).to have_key(:test_option) expect(strategy.options[:deep_option]).to have_key(:test_option)
end end
end end
@ -771,6 +885,12 @@ describe OmniAuth::Strategy do
expect(strategy.env['omniauth.params']).to eq('foo' => 'bar') expect(strategy.env['omniauth.params']).to eq('foo' => 'bar')
end end
it 'rescues errors in request_call' do
allow(strategy).to receive(:mock_request_call).and_raise(StandardError.new('Oh no'))
expect(strategy).to receive(:fail!).with('Oh no', kind_of(StandardError))
strategy.call(make_env)
end
after do after do
OmniAuth.config.test_mode = false OmniAuth.config.test_mode = false
end end
@ -809,6 +929,65 @@ describe OmniAuth::Strategy do
OmniAuth.config.test_mode = false OmniAuth.config.test_mode = false
end end
end end
context 'authenticity validation' do
let(:app) { lambda { |_env| [200, {}, ['reached our target']] } }
let(:strategy) { ExampleStrategy.new(app, :request_path => '/auth/test') }
before do
OmniAuth.config.request_validation_phase = OmniAuth::AuthenticityTokenProtection
end
context 'with default POST only request methods' do
let!(:csrf_token) { SecureRandom.base64(32) }
let(:escaped_token) { URI.encode_www_form_component(csrf_token, Encoding::UTF_8) }
it 'allows a request with matching authenticity_token' do
expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
post_env = make_env('/auth/test', 'rack.session' => {:csrf => csrf_token}, 'rack.input' => StringIO.new("authenticity_token=#{escaped_token}"))
strategy.call(post_env)
end
it 'does not allow a request without a matching authenticity token' do
post_env = make_env('/auth/test', 'rack.input' => StringIO.new("authenticity_token=#{escaped_token}"))
expect(strategy.call(post_env)[0]).to eq(302)
expect(strategy.call(post_env)[2]).to eq(['302 Moved'])
end
end
context 'with allowed GET' do
before(:context) do
@old_allowed_request_methods = OmniAuth.config.allowed_request_methods
OmniAuth.config.allowed_request_methods = %i[post get]
end
it 'allows a request without authenticity token' do
expect(strategy).to receive(:fail!).with('Request Phase', kind_of(StandardError))
get_env = make_env('/auth/test', 'REQUEST_METHOD' => 'GET')
strategy.call(get_env)
end
after(:context) do
OmniAuth.config.allowed_request_methods = @old_allowed_request_methods
end
end
after do
OmniAuth.config.request_validation_phase = nil
end
end
it 'calls fail! when encountering an unhandled exception' do
allow(strategy).to receive(:request_phase).and_raise(Errno::ECONNREFUSED)
expect(strategy).to receive(:fail!).with('Connection refused', kind_of(Errno::ECONNREFUSED))
strategy.call(make_env)
end
it 'redirects to the fail! result when encountering an unhandled exception' do
OmniAuth.config.test_mode = false
expect(strategy.call(make_env).first).to eq 302
end
end end
context 'setup phase' do context 'setup phase' do

View file

@ -26,20 +26,22 @@ describe OmniAuth do
end end
before do before do
@old_path_prefix = OmniAuth.config.path_prefix @old_path_prefix = OmniAuth.config.path_prefix
@old_on_failure = OmniAuth.config.on_failure @old_on_failure = OmniAuth.config.on_failure
@old_before_callback_phase = OmniAuth.config.before_callback_phase @old_before_callback_phase = OmniAuth.config.before_callback_phase
@old_before_options_phase = OmniAuth.config.before_options_phase @old_before_options_phase = OmniAuth.config.before_options_phase
@old_before_request_phase = OmniAuth.config.before_request_phase @old_before_request_phase = OmniAuth.config.before_request_phase
@old_request_validation_phase = OmniAuth.config.request_validation_phase
end end
after do after do
OmniAuth.configure do |config| OmniAuth.configure do |config|
config.path_prefix = @old_path_prefix config.path_prefix = @old_path_prefix
config.on_failure = @old_on_failure config.on_failure = @old_on_failure
config.before_callback_phase = @old_before_callback_phase config.before_callback_phase = @old_before_callback_phase
config.before_options_phase = @old_before_options_phase config.before_options_phase = @old_before_options_phase
config.before_request_phase = @old_before_request_phase config.before_request_phase = @old_before_request_phase
config.request_validation_phase = @old_request_validation_phase
end end
end end
@ -88,6 +90,15 @@ describe OmniAuth do
expect(OmniAuth.config.before_callback_phase.call).to eq('heyhey') expect(OmniAuth.config.before_callback_phase.call).to eq('heyhey')
end end
it 'is able to set request_validation_phase' do
OmniAuth.configure do |config|
config.request_validation_phase do
'validated'
end
end
expect(OmniAuth.config.request_validation_phase.call).to eq('validated')
end
describe 'mock auth' do describe 'mock auth' do
before do before do
@auth_hash = {:uid => '12345', :info => {:name => 'Joe', :email => 'joe@example.com'}} @auth_hash = {:uid => '12345', :info => {:name => 'Joe', :email => 'joe@example.com'}}
@ -128,6 +139,13 @@ describe OmniAuth do
end end
describe '::Utils' do describe '::Utils' do
describe 'form_css' do
it 'returns a style tag with the configured form_css' do
allow(OmniAuth).to receive(:config).and_return(double(:form_css => 'css.css'))
expect(OmniAuth::Utils.form_css).to eq "<style type='text/css'>css.css</style>"
end
end
describe '.deep_merge' do describe '.deep_merge' do
it 'combines hashes' do it 'combines hashes' do
expect(OmniAuth::Utils.deep_merge({'abc' => {'def' => 123}}, 'abc' => {'foo' => 'bar'})).to eq('abc' => {'def' => 123, 'foo' => 'bar'}) expect(OmniAuth::Utils.deep_merge({'abc' => {'def' => 123}}, 'abc' => {'foo' => 'bar'})).to eq('abc' => {'def' => 123, 'foo' => 'bar'})
@ -148,6 +166,15 @@ describe OmniAuth do
OmniAuth.config.add_camelization('oauth', 'OAuth') OmniAuth.config.add_camelization('oauth', 'OAuth')
expect(OmniAuth::Utils.camelize(:oauth)).to eq('OAuth') expect(OmniAuth::Utils.camelize(:oauth)).to eq('OAuth')
end end
it 'doesn\'t uppercase the first letter when passed false' do
expect(OmniAuth::Utils.camelize('apple_jack', false)).to eq('appleJack')
end
it 'replaces / with ::' do
expect(OmniAuth::Utils.camelize('apple_jack/cereal')).to eq('AppleJack::Cereal')
expect(OmniAuth::Utils.camelize('apple_jack/cereal', false)).to eq('appleJack::Cereal')
end
end end
end end
end end