oauth2 1.4.6 → 1.4.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -1
  3. data/README.md +1 -0
  4. data/lib/oauth2/access_token.rb +3 -5
  5. data/lib/oauth2/version.rb +7 -3
  6. data/spec/helper.rb +37 -0
  7. data/spec/oauth2/access_token_spec.rb +216 -0
  8. data/spec/oauth2/authenticator_spec.rb +84 -0
  9. data/spec/oauth2/client_spec.rb +506 -0
  10. data/spec/oauth2/mac_token_spec.rb +117 -0
  11. data/spec/oauth2/response_spec.rb +90 -0
  12. data/spec/oauth2/strategy/assertion_spec.rb +58 -0
  13. data/spec/oauth2/strategy/auth_code_spec.rb +107 -0
  14. data/spec/oauth2/strategy/base_spec.rb +5 -0
  15. data/spec/oauth2/strategy/client_credentials_spec.rb +69 -0
  16. data/spec/oauth2/strategy/implicit_spec.rb +26 -0
  17. data/spec/oauth2/strategy/password_spec.rb +55 -0
  18. data/spec/oauth2/version_spec.rb +23 -0
  19. metadata +31 -31
  20. data/.document +0 -5
  21. data/.github/dependabot.yml +0 -8
  22. data/.github/workflows/style.yml +0 -37
  23. data/.github/workflows/test.yml +0 -58
  24. data/.gitignore +0 -19
  25. data/.jrubyrc +0 -1
  26. data/.rspec +0 -4
  27. data/.rubocop.yml +0 -112
  28. data/.rubocop_rspec.yml +0 -26
  29. data/.rubocop_todo.yml +0 -113
  30. data/.ruby-version +0 -1
  31. data/.travis.yml +0 -75
  32. data/CONTRIBUTING.md +0 -18
  33. data/Gemfile +0 -61
  34. data/Rakefile +0 -45
  35. data/gemfiles/jruby_1.7.gemfile +0 -11
  36. data/gemfiles/jruby_9.0.gemfile +0 -7
  37. data/gemfiles/jruby_9.1.gemfile +0 -3
  38. data/gemfiles/jruby_9.2.gemfile +0 -3
  39. data/gemfiles/jruby_head.gemfile +0 -3
  40. data/gemfiles/ruby_1.9.gemfile +0 -11
  41. data/gemfiles/ruby_2.0.gemfile +0 -6
  42. data/gemfiles/ruby_head.gemfile +0 -9
  43. data/gemfiles/truffleruby.gemfile +0 -3
  44. data/maintenance-branch +0 -1
  45. data/oauth2.gemspec +0 -52
@@ -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