oauth2 1.4.2 → 1.4.7

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -2
  3. data/CODE_OF_CONDUCT.md +105 -46
  4. data/README.md +33 -5
  5. data/lib/oauth2/access_token.rb +12 -4
  6. data/lib/oauth2/authenticator.rb +10 -0
  7. data/lib/oauth2/client.rb +63 -16
  8. data/lib/oauth2/mac_token.rb +10 -2
  9. data/lib/oauth2/response.rb +5 -3
  10. data/lib/oauth2/strategy/assertion.rb +3 -3
  11. data/lib/oauth2/strategy/password.rb +2 -2
  12. data/lib/oauth2/version.rb +9 -3
  13. data/spec/helper.rb +37 -0
  14. data/spec/oauth2/access_token_spec.rb +216 -0
  15. data/spec/oauth2/authenticator_spec.rb +84 -0
  16. data/spec/oauth2/client_spec.rb +506 -0
  17. data/spec/oauth2/mac_token_spec.rb +117 -0
  18. data/spec/oauth2/response_spec.rb +90 -0
  19. data/spec/oauth2/strategy/assertion_spec.rb +58 -0
  20. data/spec/oauth2/strategy/auth_code_spec.rb +107 -0
  21. data/spec/oauth2/strategy/base_spec.rb +5 -0
  22. data/spec/oauth2/strategy/client_credentials_spec.rb +69 -0
  23. data/spec/oauth2/strategy/implicit_spec.rb +26 -0
  24. data/spec/oauth2/strategy/password_spec.rb +55 -0
  25. data/spec/oauth2/version_spec.rb +23 -0
  26. metadata +41 -38
  27. data/.document +0 -5
  28. data/.gitignore +0 -19
  29. data/.jrubyrc +0 -1
  30. data/.rspec +0 -2
  31. data/.rubocop.yml +0 -80
  32. data/.rubocop_rspec.yml +0 -26
  33. data/.rubocop_todo.yml +0 -15
  34. data/.ruby-version +0 -1
  35. data/.travis.yml +0 -70
  36. data/CONTRIBUTING.md +0 -18
  37. data/Gemfile +0 -40
  38. data/Rakefile +0 -45
  39. data/gemfiles/jruby_1.7.gemfile +0 -11
  40. data/gemfiles/jruby_9.0.gemfile +0 -7
  41. data/gemfiles/jruby_9.1.gemfile +0 -3
  42. data/gemfiles/jruby_9.2.gemfile +0 -3
  43. data/gemfiles/jruby_head.gemfile +0 -3
  44. data/gemfiles/ruby_1.9.gemfile +0 -11
  45. data/gemfiles/ruby_2.0.gemfile +0 -6
  46. data/gemfiles/ruby_2.1.gemfile +0 -6
  47. data/gemfiles/ruby_2.2.gemfile +0 -3
  48. data/gemfiles/ruby_2.3.gemfile +0 -3
  49. data/gemfiles/ruby_2.4.gemfile +0 -3
  50. data/gemfiles/ruby_2.5.gemfile +0 -3
  51. data/gemfiles/ruby_2.6.gemfile +0 -9
  52. data/gemfiles/ruby_head.gemfile +0 -9
  53. data/gemfiles/truffleruby.gemfile +0 -3
  54. data/oauth2.gemspec +0 -44
@@ -0,0 +1,117 @@
1
+ describe OAuth2::MACToken do
2
+ subject { described_class.new(client, token, 'abc123') }
3
+
4
+ let(:token) { 'monkey' }
5
+ let(:client) do
6
+ OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com') do |builder|
7
+ builder.request :url_encoded
8
+ builder.adapter :test do |stub|
9
+ VERBS.each do |verb|
10
+ stub.send(verb, '/token/header') { |env| [200, {}, env[:request_headers]['Authorization']] }
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ describe '#initialize' do
17
+ it 'assigns client and token' do
18
+ expect(subject.client).to eq(client)
19
+ expect(subject.token).to eq(token)
20
+ end
21
+
22
+ it 'assigns secret' do
23
+ expect(subject.secret).to eq('abc123')
24
+ end
25
+
26
+ it 'defaults algorithm to hmac-sha-256' do
27
+ expect(subject.algorithm).to be_instance_of(OpenSSL::Digest::SHA256)
28
+ end
29
+
30
+ it 'handles hmac-sha-256' do
31
+ mac = described_class.new(client, token, 'abc123', :algorithm => 'hmac-sha-256')
32
+ expect(mac.algorithm).to be_instance_of(OpenSSL::Digest::SHA256)
33
+ end
34
+
35
+ it 'handles hmac-sha-1' do
36
+ mac = described_class.new(client, token, 'abc123', :algorithm => 'hmac-sha-1')
37
+ expect(mac.algorithm).to be_instance_of(OpenSSL::Digest::SHA1)
38
+ end
39
+
40
+ it 'raises on improper algorithm' do
41
+ expect { described_class.new(client, token, 'abc123', :algorithm => 'invalid-sha') }.to raise_error(ArgumentError)
42
+ end
43
+ end
44
+
45
+ describe '#request' do
46
+ VERBS.each do |verb|
47
+ it "sends the token in the Authorization header for a #{verb.to_s.upcase} request" do
48
+ expect(subject.post('/token/header').body).to include("MAC id=\"#{token}\"")
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '#header' do
54
+ it 'does not generate the same header twice' do
55
+ header = subject.header('get', 'https://www.example.com/hello')
56
+ duplicate_header = subject.header('get', 'https://www.example.com/hello')
57
+
58
+ expect(header).not_to eq(duplicate_header)
59
+ end
60
+
61
+ it 'generates the proper format' do
62
+ header = subject.header('get', 'https://www.example.com/hello?a=1')
63
+ expect(header).to match(/MAC id="#{token}", ts="[0-9]+", nonce="[^"]+", mac="[^"]+"/)
64
+ end
65
+
66
+ it 'passes ArgumentError with an invalid url' do
67
+ expect { subject.header('get', 'this-is-not-valid') }.to raise_error(ArgumentError)
68
+ end
69
+
70
+ it 'passes URI::InvalidURIError through' do
71
+ expect { subject.header('get', nil) }.to raise_error(URI::InvalidURIError)
72
+ end
73
+ end
74
+
75
+ describe '#signature' do
76
+ it 'generates properly' do
77
+ signature = subject.signature(0, 'random-string', 'get', URI('https://www.google.com'))
78
+ expect(signature).to eq('rMDjVA3VJj3v1OmxM29QQljKia6msl5rjN83x3bZmi8=')
79
+ end
80
+ end
81
+
82
+ describe '#headers' do
83
+ it 'is an empty hash' do
84
+ expect(subject.headers).to eq({})
85
+ end
86
+ end
87
+
88
+ describe '.from_access_token' do
89
+ subject { described_class.from_access_token(access_token, 'hello') }
90
+
91
+ let(:access_token) do
92
+ OAuth2::AccessToken.new(
93
+ client, token,
94
+ :expires_at => 1,
95
+ :expires_in => 1,
96
+ :refresh_token => 'abc',
97
+ :random => 1
98
+ )
99
+ end
100
+
101
+ it 'initializes client, token, and secret properly' do
102
+ expect(subject.client).to eq(client)
103
+ expect(subject.token).to eq(token)
104
+ expect(subject.secret).to eq('hello')
105
+ end
106
+
107
+ it 'initializes configuration options' do
108
+ expect(subject.expires_at).to eq(1)
109
+ expect(subject.expires_in).to eq(1)
110
+ expect(subject.refresh_token).to eq('abc')
111
+ end
112
+
113
+ it 'initializes params' do
114
+ expect(subject.params).to eq(:random => 1)
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,90 @@
1
+ describe OAuth2::Response do
2
+ describe '#initialize' do
3
+ let(:status) { 200 }
4
+ let(:headers) { {'foo' => 'bar'} }
5
+ let(:body) { 'foo' }
6
+
7
+ it 'returns the status, headers and body' do
8
+ response = double('response', :headers => headers,
9
+ :status => status,
10
+ :body => body)
11
+ subject = described_class.new(response)
12
+ expect(subject.headers).to eq(headers)
13
+ expect(subject.status).to eq(status)
14
+ expect(subject.body).to eq(body)
15
+ end
16
+ end
17
+
18
+ describe '.register_parser' do
19
+ let(:response) do
20
+ double('response', :headers => {'Content-Type' => 'application/foo-bar'},
21
+ :status => 200,
22
+ :body => 'baz')
23
+ end
24
+
25
+ before do
26
+ described_class.register_parser(:foobar, 'application/foo-bar') do |body|
27
+ "foobar #{body}"
28
+ end
29
+ end
30
+
31
+ it 'adds to the content types and parsers' do
32
+ expect(described_class.send(:class_variable_get, :@@parsers).keys).to include(:foobar)
33
+ expect(described_class.send(:class_variable_get, :@@content_types).keys).to include('application/foo-bar')
34
+ end
35
+
36
+ it 'is able to parse that content type automatically' do
37
+ expect(described_class.new(response).parsed).to eq('foobar baz')
38
+ end
39
+ end
40
+
41
+ describe '#parsed' do
42
+ it 'parses application/x-www-form-urlencoded body' do
43
+ headers = {'Content-Type' => 'application/x-www-form-urlencoded'}
44
+ body = 'foo=bar&answer=42'
45
+ response = double('response', :headers => headers, :body => body)
46
+ subject = described_class.new(response)
47
+ expect(subject.parsed.keys.size).to eq(2)
48
+ expect(subject.parsed['foo']).to eq('bar')
49
+ expect(subject.parsed['answer']).to eq('42')
50
+ end
51
+
52
+ it 'parses application/json body' do
53
+ headers = {'Content-Type' => 'application/json'}
54
+ body = MultiJson.encode(:foo => 'bar', :answer => 42)
55
+ response = double('response', :headers => headers, :body => body)
56
+ subject = described_class.new(response)
57
+ expect(subject.parsed.keys.size).to eq(2)
58
+ expect(subject.parsed['foo']).to eq('bar')
59
+ expect(subject.parsed['answer']).to eq(42)
60
+ end
61
+
62
+ it "doesn't try to parse other content-types" do
63
+ headers = {'Content-Type' => 'text/html'}
64
+ body = '<!DOCTYPE html><html><head></head><body></body></html>'
65
+
66
+ response = double('response', :headers => headers, :body => body)
67
+
68
+ expect(MultiJson).not_to receive(:decode)
69
+ expect(MultiJson).not_to receive(:load)
70
+ expect(Rack::Utils).not_to receive(:parse_query)
71
+
72
+ subject = described_class.new(response)
73
+ expect(subject.parsed).to be_nil
74
+ end
75
+ end
76
+
77
+ context 'with xml parser registration' do
78
+ it 'tries to load multi_xml and use it' do
79
+ expect(described_class.send(:class_variable_get, :@@parsers)[:xml]).not_to be_nil
80
+ end
81
+
82
+ it 'is able to parse xml' do
83
+ headers = {'Content-Type' => 'text/xml'}
84
+ body = '<?xml version="1.0" standalone="yes" ?><foo><bar>baz</bar></foo>'
85
+
86
+ response = double('response', :headers => headers, :body => body)
87
+ expect(described_class.new(response).parsed).to eq('foo' => {'bar' => 'baz'})
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,58 @@
1
+ describe OAuth2::Strategy::Assertion do
2
+ subject { client.assertion }
3
+
4
+ let(:client) do
5
+ cli = OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com')
6
+ cli.connection.build do |b|
7
+ b.adapter :test do |stub|
8
+ stub.post('/oauth/token') do |env|
9
+ case @mode
10
+ when 'formencoded'
11
+ [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, 'expires_in=600&access_token=salmon&refresh_token=trout']
12
+ when 'json'
13
+ [200, {'Content-Type' => 'application/json'}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}']
14
+ end
15
+ end
16
+ end
17
+ end
18
+ cli
19
+ end
20
+
21
+ let(:params) do
22
+ {
23
+ :hmac_secret => 'foo',
24
+ :exp => Time.now.utc.to_i + 3600,
25
+ }
26
+ end
27
+
28
+ describe '#authorize_url' do
29
+ it 'raises NotImplementedError' do
30
+ expect { subject.authorize_url }.to raise_error(NotImplementedError)
31
+ end
32
+ end
33
+
34
+ %w[json formencoded].each do |mode|
35
+ describe "#get_token (#{mode})" do
36
+ before do
37
+ @mode = mode
38
+ @access = subject.get_token(params)
39
+ end
40
+
41
+ it 'returns AccessToken with same Client' do
42
+ expect(@access.client).to eq(client)
43
+ end
44
+
45
+ it 'returns AccessToken with #token' do
46
+ expect(@access.token).to eq('salmon')
47
+ end
48
+
49
+ it 'returns AccessToken with #expires_in' do
50
+ expect(@access.expires_in).to eq(600)
51
+ end
52
+
53
+ it 'returns AccessToken with #expires_at' do
54
+ expect(@access.expires_at).not_to be_nil
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,107 @@
1
+ # encoding: utf-8
2
+
3
+ describe OAuth2::Strategy::AuthCode do
4
+ subject { client.auth_code }
5
+
6
+ let(:code) { 'sushi' }
7
+ let(:kvform_token) { 'expires_in=600&access_token=salmon&refresh_token=trout&extra_param=steve' }
8
+ let(:facebook_token) { kvform_token.gsub('_in', '') }
9
+ let(:json_token) { MultiJson.encode(:expires_in => 600, :access_token => 'salmon', :refresh_token => 'trout', :extra_param => 'steve') }
10
+
11
+ let(:client) do
12
+ OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com') do |builder|
13
+ builder.adapter :test do |stub|
14
+ stub.get("/oauth/token?client_id=abc&client_secret=def&code=#{code}&grant_type=authorization_code") do |env|
15
+ case @mode
16
+ when 'formencoded'
17
+ [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token]
18
+ when 'json'
19
+ [200, {'Content-Type' => 'application/json'}, json_token]
20
+ when 'from_facebook'
21
+ [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, facebook_token]
22
+ end
23
+ end
24
+ stub.post('/oauth/token', 'client_id' => 'abc', 'client_secret' => 'def', 'code' => 'sushi', 'grant_type' => 'authorization_code') do |env|
25
+ case @mode
26
+ when 'formencoded'
27
+ [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token]
28
+ when 'json'
29
+ [200, {'Content-Type' => 'application/json'}, json_token]
30
+ when 'from_facebook'
31
+ [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, facebook_token]
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ describe '#authorize_url' do
39
+ it 'includes the client_id' do
40
+ expect(subject.authorize_url).to include('client_id=abc')
41
+ end
42
+
43
+ it 'includes the type' do
44
+ expect(subject.authorize_url).to include('response_type=code')
45
+ end
46
+
47
+ it 'includes passed in options' do
48
+ cb = 'http://myserver.local/oauth/callback'
49
+ expect(subject.authorize_url(:redirect_uri => cb)).to include("redirect_uri=#{Rack::Utils.escape(cb)}")
50
+ end
51
+ end
52
+
53
+ describe '#get_token (handling utf-8 data)' do
54
+ let(:json_token) { MultiJson.encode(:expires_in => 600, :access_token => 'salmon', :refresh_token => 'trout', :extra_param => 'André') }
55
+
56
+ before do
57
+ @mode = 'json'
58
+ client.options[:token_method] = :post
59
+ end
60
+
61
+ it 'does not raise an error' do
62
+ expect { subject.get_token(code) }.not_to raise_error
63
+ end
64
+
65
+ it 'does not create an error instance' do
66
+ expect(OAuth2::Error).not_to receive(:new)
67
+
68
+ subject.get_token(code)
69
+ end
70
+ end
71
+
72
+ %w[json formencoded from_facebook].each do |mode|
73
+ [:get, :post].each do |verb|
74
+ describe "#get_token (#{mode}, access_token_method=#{verb}" do
75
+ before do
76
+ @mode = mode
77
+ client.options[:token_method] = verb
78
+ @access = subject.get_token(code)
79
+ end
80
+
81
+ it 'returns AccessToken with same Client' do
82
+ expect(@access.client).to eq(client)
83
+ end
84
+
85
+ it 'returns AccessToken with #token' do
86
+ expect(@access.token).to eq('salmon')
87
+ end
88
+
89
+ it 'returns AccessToken with #refresh_token' do
90
+ expect(@access.refresh_token).to eq('trout')
91
+ end
92
+
93
+ it 'returns AccessToken with #expires_in' do
94
+ expect(@access.expires_in).to eq(600)
95
+ end
96
+
97
+ it 'returns AccessToken with #expires_at' do
98
+ expect(@access.expires_at).to be_kind_of(Integer)
99
+ end
100
+
101
+ it 'returns AccessToken with params accessible via []' do
102
+ expect(@access['extra_param']).to eq('steve')
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,5 @@
1
+ describe OAuth2::Strategy::Base do
2
+ it 'initializes with a Client' do
3
+ expect { described_class.new(OAuth2::Client.new('abc', 'def')) }.not_to raise_error
4
+ end
5
+ end
@@ -0,0 +1,69 @@
1
+ describe OAuth2::Strategy::ClientCredentials do
2
+ subject { client.client_credentials }
3
+
4
+ let(:kvform_token) { 'expires_in=600&access_token=salmon&refresh_token=trout' }
5
+ let(:json_token) { '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}' }
6
+
7
+ let(:client) do
8
+ OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com') do |builder|
9
+ builder.adapter :test do |stub|
10
+ stub.post('/oauth/token', 'grant_type' => 'client_credentials') do |env|
11
+ client_id, client_secret = Base64.decode64(env[:request_headers]['Authorization'].split(' ', 2)[1]).split(':', 2)
12
+ client_id == 'abc' && client_secret == 'def' || raise(Faraday::Adapter::Test::Stubs::NotFound)
13
+ case @mode
14
+ when 'formencoded'
15
+ [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token]
16
+ when 'json'
17
+ [200, {'Content-Type' => 'application/json'}, json_token]
18
+ end
19
+ end
20
+ stub.post('/oauth/token', 'client_id' => 'abc', 'client_secret' => 'def', 'grant_type' => 'client_credentials') do |env|
21
+ case @mode
22
+ when 'formencoded'
23
+ [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token]
24
+ when 'json'
25
+ [200, {'Content-Type' => 'application/json'}, json_token]
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ describe '#authorize_url' do
33
+ it 'raises NotImplementedError' do
34
+ expect { subject.authorize_url }.to raise_error(NotImplementedError)
35
+ end
36
+ end
37
+
38
+ %w[json formencoded].each do |mode|
39
+ [:basic_auth, :request_body].each do |auth_scheme|
40
+ describe "#get_token (#{mode}) (#{auth_scheme})" do
41
+ before do
42
+ @mode = mode
43
+ client.options[:auth_scheme] = auth_scheme
44
+ @access = subject.get_token
45
+ end
46
+
47
+ it 'returns AccessToken with same Client' do
48
+ expect(@access.client).to eq(client)
49
+ end
50
+
51
+ it 'returns AccessToken with #token' do
52
+ expect(@access.token).to eq('salmon')
53
+ end
54
+
55
+ it 'returns AccessToken without #refresh_token' do
56
+ expect(@access.refresh_token).to be_nil
57
+ end
58
+
59
+ it 'returns AccessToken with #expires_in' do
60
+ expect(@access.expires_in).to eq(600)
61
+ end
62
+
63
+ it 'returns AccessToken with #expires_at' do
64
+ expect(@access.expires_at).not_to be_nil
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end