omniauth-oauthio 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/Gemfile +7 -0
- data/LICENSE +20 -0
- data/README.md +118 -0
- data/Rakefile +8 -0
- data/example/Gemfile +4 -0
- data/example/Gemfile.lock +44 -0
- data/example/config.ru +110 -0
- data/lib/oauthio/access_token.rb +47 -0
- data/lib/oauthio/client.rb +158 -0
- data/lib/oauthio/providers/oauthio.rb +84 -0
- data/lib/oauthio/strategy/auth_code.rb +36 -0
- data/lib/omniauth/oauthio/version.rb +5 -0
- data/lib/omniauth/oauthio.rb +9 -0
- data/lib/omniauth/strategies/oauthio.rb +158 -0
- data/lib/omniauth-oauthio.rb +1 -0
- data/omniauth-oauthio.gemspec +24 -0
- data/test/helper.rb +56 -0
- data/test/support/shared_examples.rb +85 -0
- data/test/test.rb +541 -0
- metadata +123 -0
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'omniauth/strategies/oauth2'
|
2
|
+
require 'base64'
|
3
|
+
require 'openssl'
|
4
|
+
require 'rack/utils'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
module OmniAuth
|
8
|
+
module Strategies
|
9
|
+
class Oauthio < OmniAuth::Strategies::OAuth2
|
10
|
+
include OmniAuth::Strategy
|
11
|
+
|
12
|
+
args [:client_id, :client_secret]
|
13
|
+
|
14
|
+
# Give your strategy a name.
|
15
|
+
option :name, "oauthio"
|
16
|
+
|
17
|
+
# This is where you pass the options you would pass when
|
18
|
+
# initializing your consumer from the OAuth gem.
|
19
|
+
option :client_options, {
|
20
|
+
:site => 'https://oauth.io',
|
21
|
+
}
|
22
|
+
|
23
|
+
option :client_id, nil
|
24
|
+
option :client_secret, nil
|
25
|
+
|
26
|
+
# Returns true if the environment recognizes either the
|
27
|
+
# request or callback path.
|
28
|
+
def on_auth_path?
|
29
|
+
on_request_path? || on_callback_path?
|
30
|
+
end
|
31
|
+
|
32
|
+
def client_with_provider(provider)
|
33
|
+
options.client_options.merge!({authorize_url: "#{options.client_options.authorization_url}/#{provider}"})
|
34
|
+
client
|
35
|
+
end
|
36
|
+
|
37
|
+
def current_path
|
38
|
+
# This might not be completely safe. I want to ensure that the current_path does not have a format at the end
|
39
|
+
# so the .json should be removed.
|
40
|
+
super.split('.').first
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_request_path?
|
44
|
+
if options.request_path.respond_to?(:call)
|
45
|
+
options.request_path.call(env)
|
46
|
+
else
|
47
|
+
on_path?(request_path)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def on_path?(path)
|
52
|
+
current_path.casecmp(path) == 0
|
53
|
+
end
|
54
|
+
|
55
|
+
def on_callback_path?
|
56
|
+
on_path?(callback_path)
|
57
|
+
end
|
58
|
+
|
59
|
+
def sub_provider
|
60
|
+
test = request.path.split("#{path_prefix}/#{name}/").last
|
61
|
+
slashes = test.split('/')
|
62
|
+
if slashes.length > 1
|
63
|
+
# return ''
|
64
|
+
return slashes.first.split('.').first
|
65
|
+
end
|
66
|
+
|
67
|
+
test.split('.').first
|
68
|
+
end
|
69
|
+
|
70
|
+
def request_path
|
71
|
+
options[:request_path].is_a?(String) ? options[:request_path] : "#{path_prefix}/#{name}/#{sub_provider}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def callback_path
|
75
|
+
path = options[:callback_path] if options[:callback_path].is_a?(String)
|
76
|
+
path ||= current_path if options[:callback_path].respond_to?(:call) && options[:callback_path].call(env)
|
77
|
+
path ||= custom_path(:request_path)
|
78
|
+
path ||= "#{path_prefix}/#{name}/#{sub_provider}/callback"
|
79
|
+
path
|
80
|
+
end
|
81
|
+
|
82
|
+
def path_prefix
|
83
|
+
options[:path_prefix] || OmniAuth.config.path_prefix
|
84
|
+
end
|
85
|
+
|
86
|
+
def request_phase
|
87
|
+
params = authorize_params
|
88
|
+
# We may want to skip redirecting the user if calling from a SPA that does not want to reload the page.
|
89
|
+
# The json option will return a json response instead of redirecting.
|
90
|
+
if request.path_info.include?('.json')
|
91
|
+
json = {state: session['omniauth.state']}.to_json
|
92
|
+
return Rack::Response.new(json, 200, 'content-type' => 'application/json').finish
|
93
|
+
end
|
94
|
+
|
95
|
+
# TODO: Check the redirect url.
|
96
|
+
provider = params[:provider]
|
97
|
+
params = params.except(:provider)
|
98
|
+
redirect_url = client_with_provider(provider).auth_code.authorize_url({:redirect_uri => callback_url}.merge(params))
|
99
|
+
redirect redirect_url
|
100
|
+
end
|
101
|
+
|
102
|
+
def auth_hash
|
103
|
+
provider_info = ::Oauthio::Providers::Oauthio.new(access_token, client.secret, options)
|
104
|
+
provider = access_token.provider
|
105
|
+
hash = AuthHash.new(:provider => provider, :uid => provider_info.uid)
|
106
|
+
hash.info = provider_info.info unless provider_info.skip_info?
|
107
|
+
hash.credentials = provider_info.credentials if provider_info.credentials
|
108
|
+
hash.extra = provider_info.extra if provider_info.extra
|
109
|
+
hash
|
110
|
+
end
|
111
|
+
|
112
|
+
def callback_phase
|
113
|
+
#if request.params['error'] || request.params['error_reason']
|
114
|
+
# raise CallbackError.new(request.params['error'], request.params['error_description'] || request.params['error_reason'], request.params['error_uri'])
|
115
|
+
#end
|
116
|
+
if !options.provider_ignores_state && !verified_state?
|
117
|
+
raise CallbackError.new(nil, :csrf_detected)
|
118
|
+
end
|
119
|
+
|
120
|
+
self.access_token = build_access_token
|
121
|
+
self.access_token = access_token.refresh! if access_token.expired?
|
122
|
+
|
123
|
+
env['omniauth.auth'] = auth_hash
|
124
|
+
# Delete the omniauth.state after we have verified all requests
|
125
|
+
session.delete('omniauth.state')
|
126
|
+
call_app!
|
127
|
+
|
128
|
+
#rescue ::Oauthio::Error, CallbackError => e
|
129
|
+
rescue CallbackError => e
|
130
|
+
fail!(:invalid_credentials, e)
|
131
|
+
rescue ::MultiJson::DecodeError => e
|
132
|
+
fail!(:invalid_response, e)
|
133
|
+
rescue ::Timeout::Error, ::Errno::ETIMEDOUT => e
|
134
|
+
fail!(:timeout, e)
|
135
|
+
rescue ::SocketError => e
|
136
|
+
fail!(:failed_to_connect, e)
|
137
|
+
end
|
138
|
+
|
139
|
+
protected
|
140
|
+
# Client should only be access via client_with_provider
|
141
|
+
def client
|
142
|
+
state = session['omniauth.state']
|
143
|
+
options.client_options[:state] = state
|
144
|
+
::Oauthio::Client.new(options.client_id, options.client_secret, deep_symbolize(options.client_options))
|
145
|
+
end
|
146
|
+
|
147
|
+
def verified_state?
|
148
|
+
state = request.params['state']
|
149
|
+
return false if state.to_s.empty?
|
150
|
+
state == session['omniauth.state']
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
|
158
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'omniauth/oauthio'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'omniauth/oauthio/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'omniauth-oauthio'
|
7
|
+
s.version = OmniAuth::Oauthio::VERSION
|
8
|
+
s.authors = ['Jonathan Rowlands']
|
9
|
+
s.email = ['jonrowlands83@gmail.com']
|
10
|
+
s.summary = 'OAuth.io Strategy for OmniAuth'
|
11
|
+
s.homepage = 'https://github.com/jgrowl/omniauth-oauthio'
|
12
|
+
s.license = 'MIT'
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
17
|
+
s.require_paths = ['lib']
|
18
|
+
|
19
|
+
s.add_runtime_dependency 'omniauth-oauth2', '~> 1.1'
|
20
|
+
|
21
|
+
s.add_development_dependency 'minitest'
|
22
|
+
s.add_development_dependency 'mocha'
|
23
|
+
s.add_development_dependency 'rake'
|
24
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
#require 'bundler/setup'
|
2
|
+
#require 'minitest/autorun'
|
3
|
+
#require 'mocha/setup'
|
4
|
+
#require 'omniauth/strategies/facebook'
|
5
|
+
#
|
6
|
+
#OmniAuth.config.test_mode = true
|
7
|
+
#
|
8
|
+
#module BlockTestHelper
|
9
|
+
# def test(name, &blk)
|
10
|
+
# method_name = "test_#{name.gsub(/\s+/, '_')}"
|
11
|
+
# raise "Method already defined: #{method_name}" if instance_methods.include?(method_name.to_sym)
|
12
|
+
# define_method method_name, &blk
|
13
|
+
# end
|
14
|
+
#end
|
15
|
+
#
|
16
|
+
#module CustomAssertions
|
17
|
+
# def assert_has_key(key, hash, msg = nil)
|
18
|
+
# msg = message(msg) { "Expected #{hash.inspect} to have key #{key.inspect}" }
|
19
|
+
# assert hash.has_key?(key), msg
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# def refute_has_key(key, hash, msg = nil)
|
23
|
+
# msg = message(msg) { "Expected #{hash.inspect} not to have key #{key.inspect}" }
|
24
|
+
# refute hash.has_key?(key), msg
|
25
|
+
# end
|
26
|
+
#end
|
27
|
+
#
|
28
|
+
#class TestCase < Minitest::Test
|
29
|
+
# extend BlockTestHelper
|
30
|
+
# include CustomAssertions
|
31
|
+
#end
|
32
|
+
#
|
33
|
+
#class StrategyTestCase < TestCase
|
34
|
+
# def setup
|
35
|
+
# @request = stub('Request')
|
36
|
+
# @request.stubs(:params).returns({})
|
37
|
+
# @request.stubs(:cookies).returns({})
|
38
|
+
# @request.stubs(:env).returns({})
|
39
|
+
# @request.stubs(:scheme).returns({})
|
40
|
+
# @request.stubs(:ssl?).returns(false)
|
41
|
+
#
|
42
|
+
# @client_id = '123'
|
43
|
+
# @client_secret = '53cr3tz'
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# def strategy
|
47
|
+
# @strategy ||= begin
|
48
|
+
# args = [@client_id, @client_secret, @options].compact
|
49
|
+
# OmniAuth::Strategies::Facebook.new(nil, *args).tap do |strategy|
|
50
|
+
# strategy.stubs(:request).returns(@request)
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
#end
|
55
|
+
#
|
56
|
+
#Dir[File.expand_path('../support/**/*', __FILE__)].each &method(:require)
|
@@ -0,0 +1,85 @@
|
|
1
|
+
## NOTE it would be useful if this lived in omniauth-Oauthio eventually
|
2
|
+
#module OauthioStrategyTests
|
3
|
+
# def self.included(base)
|
4
|
+
# base.class_eval do
|
5
|
+
# include ClientTests
|
6
|
+
# include AuthorizeParamsTests
|
7
|
+
# include CSRFAuthorizeParamsTests
|
8
|
+
# include TokenParamsTests
|
9
|
+
# end
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# module ClientTests
|
13
|
+
# extend BlockTestHelper
|
14
|
+
#
|
15
|
+
# test 'should be initialized with symbolized client_options' do
|
16
|
+
# @options = { :client_options => { 'authorize_url' => 'https://example.com' } }
|
17
|
+
# assert_equal 'https://example.com', strategy.client.options[:authorize_url]
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# module AuthorizeParamsTests
|
22
|
+
# extend BlockTestHelper
|
23
|
+
#
|
24
|
+
# test 'should include any authorize params passed in the :authorize_params option' do
|
25
|
+
# @options = { :authorize_params => { :foo => 'bar', :baz => 'zip' } }
|
26
|
+
# assert_equal 'bar', strategy.authorize_params['foo']
|
27
|
+
# assert_equal 'zip', strategy.authorize_params['baz']
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# test 'should include top-level options that are marked as :authorize_options' do
|
31
|
+
# @options = { :authorize_options => [:scope, :foo], :scope => 'bar', :foo => 'baz' }
|
32
|
+
# assert_equal 'bar', strategy.authorize_params['scope']
|
33
|
+
# assert_equal 'baz', strategy.authorize_params['foo']
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# test 'should exclude top-level options that are not passed' do
|
37
|
+
# @options = { :authorize_options => [:bar] }
|
38
|
+
# refute_has_key :bar, strategy.authorize_params
|
39
|
+
# refute_has_key 'bar', strategy.authorize_params
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# module CSRFAuthorizeParamsTests
|
44
|
+
# extend BlockTestHelper
|
45
|
+
#
|
46
|
+
# test 'should store random state in the session when none is present in authorize or request params' do
|
47
|
+
# assert_includes strategy.authorize_params.keys, 'state'
|
48
|
+
# refute_empty strategy.authorize_params['state']
|
49
|
+
# refute_empty strategy.session['omniauth.state']
|
50
|
+
# assert_equal strategy.authorize_params['state'], strategy.session['omniauth.state']
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# test 'should not store state in the session when present in authorize params vs. a random one' do
|
54
|
+
# @options = { :authorize_params => { :state => 'bar' } }
|
55
|
+
# refute_empty strategy.authorize_params['state']
|
56
|
+
# refute_equal 'bar', strategy.authorize_params[:state]
|
57
|
+
# refute_empty strategy.session['omniauth.state']
|
58
|
+
# refute_equal 'bar', strategy.session['omniauth.state']
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# test 'should not store state in the session when present in request params vs. a random one' do
|
62
|
+
# @request.stubs(:params).returns({ 'state' => 'foo' })
|
63
|
+
# refute_empty strategy.authorize_params['state']
|
64
|
+
# refute_equal 'foo', strategy.authorize_params[:state]
|
65
|
+
# refute_empty strategy.session['omniauth.state']
|
66
|
+
# refute_equal 'foo', strategy.session['omniauth.state']
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# module TokenParamsTests
|
71
|
+
# extend BlockTestHelper
|
72
|
+
#
|
73
|
+
# test 'should include any authorize params passed in the :token_params option' do
|
74
|
+
# @options = { :token_params => { :foo => 'bar', :baz => 'zip' } }
|
75
|
+
# assert_equal 'bar', strategy.token_params['foo']
|
76
|
+
# assert_equal 'zip', strategy.token_params['baz']
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# test 'should include top-level options that are marked as :token_options' do
|
80
|
+
# @options = { :token_options => [:scope, :foo], :scope => 'bar', :foo => 'baz' }
|
81
|
+
# assert_equal 'bar', strategy.token_params['scope']
|
82
|
+
# assert_equal 'baz', strategy.token_params['foo']
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
#end
|