rack-oauth2-revibe 1.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +7 -0
- data/LICENSE +20 -0
- data/README.rdoc +78 -0
- data/Rakefile +25 -0
- data/VERSION +1 -0
- data/lib/rack/oauth2.rb +67 -0
- data/lib/rack/oauth2/access_token.rb +36 -0
- data/lib/rack/oauth2/access_token/authenticator.rb +24 -0
- data/lib/rack/oauth2/access_token/bearer.rb +11 -0
- data/lib/rack/oauth2/access_token/legacy.rb +23 -0
- data/lib/rack/oauth2/access_token/mac.rb +103 -0
- data/lib/rack/oauth2/access_token/mac/sha256_hex_verifier.rb +17 -0
- data/lib/rack/oauth2/access_token/mac/signature.rb +34 -0
- data/lib/rack/oauth2/access_token/mac/verifier.rb +44 -0
- data/lib/rack/oauth2/client.rb +139 -0
- data/lib/rack/oauth2/client/error.rb +14 -0
- data/lib/rack/oauth2/client/grant.rb +30 -0
- data/lib/rack/oauth2/client/grant/authorization_code.rb +12 -0
- data/lib/rack/oauth2/client/grant/client_credentials.rb +10 -0
- data/lib/rack/oauth2/client/grant/facebook_token.rb +12 -0
- data/lib/rack/oauth2/client/grant/password.rb +11 -0
- data/lib/rack/oauth2/client/grant/refresh_token.rb +11 -0
- data/lib/rack/oauth2/debugger.rb +3 -0
- data/lib/rack/oauth2/debugger/request_filter.rb +30 -0
- data/lib/rack/oauth2/server.rb +4 -0
- data/lib/rack/oauth2/server/abstract.rb +4 -0
- data/lib/rack/oauth2/server/abstract/error.rb +69 -0
- data/lib/rack/oauth2/server/abstract/handler.rb +20 -0
- data/lib/rack/oauth2/server/abstract/request.rb +29 -0
- data/lib/rack/oauth2/server/abstract/response.rb +15 -0
- data/lib/rack/oauth2/server/authorize.rb +117 -0
- data/lib/rack/oauth2/server/authorize/code.rb +39 -0
- data/lib/rack/oauth2/server/authorize/error.rb +71 -0
- data/lib/rack/oauth2/server/authorize/extension.rb +12 -0
- data/lib/rack/oauth2/server/authorize/extension/code_and_token.rb +39 -0
- data/lib/rack/oauth2/server/authorize/token.rb +43 -0
- data/lib/rack/oauth2/server/resource.rb +55 -0
- data/lib/rack/oauth2/server/resource/bearer.rb +47 -0
- data/lib/rack/oauth2/server/resource/bearer/error.rb +24 -0
- data/lib/rack/oauth2/server/resource/error.rb +81 -0
- data/lib/rack/oauth2/server/resource/mac.rb +36 -0
- data/lib/rack/oauth2/server/resource/mac/error.rb +24 -0
- data/lib/rack/oauth2/server/token.rb +87 -0
- data/lib/rack/oauth2/server/token/authorization_code.rb +28 -0
- data/lib/rack/oauth2/server/token/client_credentials.rb +23 -0
- data/lib/rack/oauth2/server/token/error.rb +54 -0
- data/lib/rack/oauth2/server/token/extension.rb +12 -0
- data/lib/rack/oauth2/server/token/extension/jwt.rb +37 -0
- data/lib/rack/oauth2/server/token/facebook_token.rb +27 -0
- data/lib/rack/oauth2/server/token/password.rb +27 -0
- data/lib/rack/oauth2/server/token/refresh_token.rb +26 -0
- data/lib/rack/oauth2/util.rb +58 -0
- data/rack-oauth2.gemspec +30 -0
- data/spec/helpers/time.rb +19 -0
- data/spec/helpers/webmock_helper.rb +41 -0
- data/spec/mock_response/blank +0 -0
- data/spec/mock_response/errors/invalid_request.json +4 -0
- data/spec/mock_response/resources/fake.txt +1 -0
- data/spec/mock_response/tokens/_Bearer.json +6 -0
- data/spec/mock_response/tokens/bearer.json +6 -0
- data/spec/mock_response/tokens/legacy.json +5 -0
- data/spec/mock_response/tokens/legacy.txt +1 -0
- data/spec/mock_response/tokens/legacy_without_expires_in.txt +1 -0
- data/spec/mock_response/tokens/mac.json +8 -0
- data/spec/mock_response/tokens/unknown.json +6 -0
- data/spec/rack/oauth2/access_token/authenticator_spec.rb +43 -0
- data/spec/rack/oauth2/access_token/bearer_spec.rb +18 -0
- data/spec/rack/oauth2/access_token/legacy_spec.rb +23 -0
- data/spec/rack/oauth2/access_token/mac/sha256_hex_verifier_spec.rb +28 -0
- data/spec/rack/oauth2/access_token/mac/signature_spec.rb +59 -0
- data/spec/rack/oauth2/access_token/mac/verifier_spec.rb +25 -0
- data/spec/rack/oauth2/access_token/mac_spec.rb +141 -0
- data/spec/rack/oauth2/access_token_spec.rb +69 -0
- data/spec/rack/oauth2/client/error_spec.rb +18 -0
- data/spec/rack/oauth2/client/grant/authorization_code_spec.rb +37 -0
- data/spec/rack/oauth2/client/grant/client_credentials_spec.rb +7 -0
- data/spec/rack/oauth2/client/grant/password_spec.rb +33 -0
- data/spec/rack/oauth2/client/grant/refresh_token_spec.rb +21 -0
- data/spec/rack/oauth2/client_spec.rb +287 -0
- data/spec/rack/oauth2/debugger/request_filter_spec.rb +33 -0
- data/spec/rack/oauth2/oauth2_spec.rb +74 -0
- data/spec/rack/oauth2/server/abstract/error_spec.rb +59 -0
- data/spec/rack/oauth2/server/authorize/code_spec.rb +57 -0
- data/spec/rack/oauth2/server/authorize/error_spec.rb +103 -0
- data/spec/rack/oauth2/server/authorize/extensions/code_and_token_spec.rb +60 -0
- data/spec/rack/oauth2/server/authorize/token_spec.rb +73 -0
- data/spec/rack/oauth2/server/authorize_spec.rb +214 -0
- data/spec/rack/oauth2/server/resource/bearer/error_spec.rb +52 -0
- data/spec/rack/oauth2/server/resource/bearer_spec.rb +123 -0
- data/spec/rack/oauth2/server/resource/error_spec.rb +147 -0
- data/spec/rack/oauth2/server/resource/mac/error_spec.rb +52 -0
- data/spec/rack/oauth2/server/resource/mac_spec.rb +119 -0
- data/spec/rack/oauth2/server/resource_spec.rb +23 -0
- data/spec/rack/oauth2/server/token/authorization_code_spec.rb +43 -0
- data/spec/rack/oauth2/server/token/client_credentials_spec.rb +23 -0
- data/spec/rack/oauth2/server/token/error_spec.rb +77 -0
- data/spec/rack/oauth2/server/token/password_spec.rb +37 -0
- data/spec/rack/oauth2/server/token/refresh_token_spec.rb +34 -0
- data/spec/rack/oauth2/server/token_spec.rb +134 -0
- data/spec/rack/oauth2/util_spec.rb +97 -0
- data/spec/spec_helper.rb +14 -0
- metadata +326 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rack::OAuth2::AccessToken::MAC::Verifier do
|
4
|
+
let(:verifier) { Rack::OAuth2::AccessToken::MAC::Verifier.new(:algorithm => algorithm) }
|
5
|
+
subject { verifier }
|
6
|
+
|
7
|
+
context 'when "hmac-sha-1" is specified' do
|
8
|
+
let(:algorithm) { 'hmac-sha-1' }
|
9
|
+
its(:hash_generator) { should be_instance_of OpenSSL::Digest::SHA1 }
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'when "hmac-sha-256" is specified' do
|
13
|
+
let(:algorithm) { 'hmac-sha-256' }
|
14
|
+
its(:hash_generator) { should be_instance_of OpenSSL::Digest::SHA256 }
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'otherwise' do
|
18
|
+
let(:algorithm) { 'invalid' }
|
19
|
+
it do
|
20
|
+
expect { verifier.send(:hash_generator) }.to raise_error(StandardError, 'Unsupported Algorithm')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rack::OAuth2::AccessToken::MAC do
|
4
|
+
let(:ts) { 1305820234 }
|
5
|
+
let :token do
|
6
|
+
Rack::OAuth2::AccessToken::MAC.new(
|
7
|
+
:access_token => 'access_token',
|
8
|
+
:mac_key => 'secret',
|
9
|
+
:mac_algorithm => 'hmac-sha-256',
|
10
|
+
:ts => ts
|
11
|
+
)
|
12
|
+
end
|
13
|
+
let :token_with_ext_verifier do
|
14
|
+
Rack::OAuth2::AccessToken::MAC.new(
|
15
|
+
:access_token => 'access_token',
|
16
|
+
:mac_key => 'secret',
|
17
|
+
:mac_algorithm => 'hmac-sha-256',
|
18
|
+
:ts => ts,
|
19
|
+
:ext_verifier => Rack::OAuth2::AccessToken::MAC::Sha256HexVerifier
|
20
|
+
)
|
21
|
+
end
|
22
|
+
let(:nonce) { '1000:51e74de734c05613f37520872e68db5f' }
|
23
|
+
let(:resource_endpoint) { 'https://server.example.com/resources/fake' }
|
24
|
+
subject { token }
|
25
|
+
|
26
|
+
its(:mac_key) { should == 'secret' }
|
27
|
+
its(:mac_algorithm) { should == 'hmac-sha-256' }
|
28
|
+
its(:token_response) do
|
29
|
+
should == {
|
30
|
+
:access_token => 'access_token',
|
31
|
+
:refresh_token => nil,
|
32
|
+
:token_type => :mac,
|
33
|
+
:expires_in => nil,
|
34
|
+
:scope => '',
|
35
|
+
:mac_key => 'secret',
|
36
|
+
:mac_algorithm => 'hmac-sha-256'
|
37
|
+
}
|
38
|
+
end
|
39
|
+
its(:generate_nonce) { should be_a String }
|
40
|
+
|
41
|
+
describe 'verify!' do
|
42
|
+
let(:request) { Rack::OAuth2::Server::Resource::MAC::Request.new(env) }
|
43
|
+
|
44
|
+
context 'when no ext_verifier is given' do
|
45
|
+
let(:env) do
|
46
|
+
Rack::MockRequest.env_for(
|
47
|
+
'/protected_resources',
|
48
|
+
'HTTP_AUTHORIZATION' => %{MAC id="access_token", nonce="#{nonce}", ts="#{ts}" mac="#{signature}"}
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when signature is valid' do
|
53
|
+
let(:signature) { 'BgooS/voPOZWLwoVfx4+zbC3xAVKW3jtjhKYOfIGZOA=' }
|
54
|
+
it do
|
55
|
+
|
56
|
+
token.verify!(request.setup!).should == :verified
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'otherwise' do
|
61
|
+
let(:signature) { 'invalid' }
|
62
|
+
it do
|
63
|
+
expect { token.verify!(request.setup!) }.to raise_error(
|
64
|
+
Rack::OAuth2::Server::Resource::MAC::Unauthorized,
|
65
|
+
'invalid_token :: Signature Invalid'
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'when ext_verifier is given' do
|
72
|
+
let(:env) do
|
73
|
+
Rack::MockRequest.env_for(
|
74
|
+
'/protected_resources',
|
75
|
+
:method => :POST,
|
76
|
+
:params => {
|
77
|
+
:key1 => 'value1'
|
78
|
+
},
|
79
|
+
'HTTP_AUTHORIZATION' => %{MAC id="access_token", nonce="#{nonce}", ts="#{ts}", mac="#{signature}", ext="#{ext}"}
|
80
|
+
)
|
81
|
+
end
|
82
|
+
let(:signature) { 'invalid' }
|
83
|
+
|
84
|
+
context 'when ext is invalid' do
|
85
|
+
let(:ext) { 'invalid' }
|
86
|
+
it do
|
87
|
+
expect { token_with_ext_verifier.verify!(request.setup!) }.to raise_error(
|
88
|
+
Rack::OAuth2::Server::Resource::MAC::Unauthorized,
|
89
|
+
'invalid_token :: Sha256HexVerifier Invalid'
|
90
|
+
)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'when ext is valid' do
|
95
|
+
let(:ext) { '4cfcd46c59f54b5ea6a5f9b05c28b52fef2864747194b5fdfc3d59c0057bf35a' }
|
96
|
+
|
97
|
+
context 'when signature is valid' do
|
98
|
+
let(:signature) { 'dZYR54n+Lym5qCRRmDqmRZ71rG+bkjSWmqrOv8OjYHk=' }
|
99
|
+
it do
|
100
|
+
Time.fix(Time.at(1302361200)) do
|
101
|
+
token_with_ext_verifier.verify!(request.setup!).should == :verified
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'otherwise' do
|
107
|
+
it do
|
108
|
+
expect { token.verify!(request.setup!) }.to raise_error(
|
109
|
+
Rack::OAuth2::Server::Resource::MAC::Unauthorized,
|
110
|
+
'invalid_token :: Signature Invalid'
|
111
|
+
)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '.authenticate' do
|
119
|
+
let(:request) { HTTPClient.new.send(:create_request, :post, URI.parse(resource_endpoint), {}, {:hello => "world"}, {}) }
|
120
|
+
context 'when no ext_verifier is given' do
|
121
|
+
let(:signature) { 'pOBaL6HRawe4tUPmcU4vJEj1f2GJqrbQOlCcdAYgI/s=' }
|
122
|
+
|
123
|
+
it 'should set Authorization header' do
|
124
|
+
token.should_receive(:generate_nonce).and_return(nonce)
|
125
|
+
request.header.should_receive(:[]=).with('Authorization', "MAC id=\"access_token\", nonce=\"#{nonce}\", ts=\"#{ts.to_i}\", mac=\"#{signature}\"")
|
126
|
+
token.authenticate(request)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'when ext_verifier is given' do
|
131
|
+
let(:signature) { 'vgU0fj6rSpwUCAoCOrXlu8pZBR8a5Q5xIVlB4MCvJeM=' }
|
132
|
+
let(:ext) { '3d011e09502a84552a0f8ae112d024cc2c115597e3a577d5f49007902c221dc5' }
|
133
|
+
it 'should set Authorization header with ext_verifier' do
|
134
|
+
token_with_ext_verifier.should_receive(:generate_nonce).and_return(nonce)
|
135
|
+
request.header.should_receive(:[]=).with('Authorization', "MAC id=\"access_token\", nonce=\"#{nonce}\", ts=\"#{ts.to_i}\", mac=\"#{signature}\", ext=\"#{ext}\"")
|
136
|
+
token_with_ext_verifier.authenticate(request)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rack::OAuth2::AccessToken do
|
4
|
+
let :token do
|
5
|
+
Rack::OAuth2::AccessToken::Bearer.new(
|
6
|
+
:access_token => 'access_token',
|
7
|
+
:refresh_token => 'refresh_token',
|
8
|
+
:expires_in => 3600,
|
9
|
+
:scope => [:scope1, :scope2]
|
10
|
+
)
|
11
|
+
end
|
12
|
+
subject { token }
|
13
|
+
|
14
|
+
its(:access_token) { should == 'access_token' }
|
15
|
+
its(:refresh_token) { should == 'refresh_token' }
|
16
|
+
its(:expires_in) { should == 3600 }
|
17
|
+
its(:scope) { should == [:scope1, :scope2] }
|
18
|
+
its(:token_response) do
|
19
|
+
should == {
|
20
|
+
:token_type => :bearer,
|
21
|
+
:access_token => 'access_token',
|
22
|
+
:refresh_token => 'refresh_token',
|
23
|
+
:expires_in => 3600,
|
24
|
+
:scope => 'scope1 scope2'
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when access_token is missing' do
|
29
|
+
it do
|
30
|
+
expect do
|
31
|
+
Rack::OAuth2::AccessToken::Bearer.new(
|
32
|
+
:refresh_token => 'refresh_token',
|
33
|
+
:expires_in => 3600,
|
34
|
+
:scope => [:scope1, :scope2]
|
35
|
+
)
|
36
|
+
end.to raise_error AttrRequired::AttrMissing
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'otherwise' do
|
41
|
+
it do
|
42
|
+
expect do
|
43
|
+
Rack::OAuth2::AccessToken::Bearer.new(
|
44
|
+
:access_token => 'access_token'
|
45
|
+
)
|
46
|
+
end.not_to raise_error
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
let(:resource_endpoint) { 'https://server.example.com/resources/fake' }
|
51
|
+
[:get, :delete, :post, :put].each do |method|
|
52
|
+
describe method do
|
53
|
+
it 'should delegate to HTTPClient with Authenticator filter' do
|
54
|
+
token.httpclient.should_receive(method).with(resource_endpoint)
|
55
|
+
token.httpclient.request_filter.last.should be_a Rack::OAuth2::AccessToken::Authenticator
|
56
|
+
token.send method, resource_endpoint
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'in debug mode' do
|
61
|
+
it do
|
62
|
+
Rack::OAuth2.debug do
|
63
|
+
token.httpclient.request_filter[-2].should be_a Rack::OAuth2::AccessToken::Authenticator
|
64
|
+
token.httpclient.request_filter.last.should be_a Rack::OAuth2::Debugger::RequestFilter
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Rack::OAuth2::Client::Error do
|
4
|
+
let :error do
|
5
|
+
{
|
6
|
+
:error => :invalid_request,
|
7
|
+
:error_description => 'Include invalid parameters',
|
8
|
+
:error_uri => 'http://server.example.com/error/invalid_request'
|
9
|
+
}
|
10
|
+
end
|
11
|
+
subject do
|
12
|
+
Rack::OAuth2::Client::Error.new 400, error
|
13
|
+
end
|
14
|
+
|
15
|
+
its(:status) { should == 400 }
|
16
|
+
its(:message) { should == error[:error_description] }
|
17
|
+
its(:response) { should == error }
|
18
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Rack::OAuth2::Client::Grant::AuthorizationCode do
|
4
|
+
let(:redirect_uri) { 'https://client.example.com/callback' }
|
5
|
+
let(:grant) { Rack::OAuth2::Client::Grant::AuthorizationCode }
|
6
|
+
|
7
|
+
context 'when code is given' do
|
8
|
+
let :attributes do
|
9
|
+
{:code => 'code'}
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'when redirect_uri is given' do
|
13
|
+
let :attributes do
|
14
|
+
{:code => 'code', :redirect_uri => redirect_uri}
|
15
|
+
end
|
16
|
+
subject { grant.new attributes }
|
17
|
+
its(:redirect_uri) { should == redirect_uri }
|
18
|
+
its(:as_json) do
|
19
|
+
should == {:grant_type => :authorization_code, :code => 'code', :redirect_uri => redirect_uri}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'otherwise' do
|
24
|
+
subject { grant.new attributes }
|
25
|
+
its(:redirect_uri) { should be_nil }
|
26
|
+
its(:as_json) do
|
27
|
+
should == {:grant_type => :authorization_code, :code => 'code', :redirect_uri => nil}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'otherwise' do
|
33
|
+
it do
|
34
|
+
expect { grant.new }.to raise_error AttrRequired::AttrMissing
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Rack::OAuth2::Client::Grant::Password do
|
4
|
+
let(:grant) { Rack::OAuth2::Client::Grant::Password }
|
5
|
+
|
6
|
+
context 'when username is given' do
|
7
|
+
let :attributes do
|
8
|
+
{:username => 'username'}
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'when password is given' do
|
12
|
+
let :attributes do
|
13
|
+
{:username => 'username', :password => 'password'}
|
14
|
+
end
|
15
|
+
subject { grant.new attributes }
|
16
|
+
its(:as_json) do
|
17
|
+
should == {:grant_type => :password, :username => 'username', :password => 'password'}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'otherwise' do
|
22
|
+
it do
|
23
|
+
expect { grant.new attributes }.to raise_error AttrRequired::AttrMissing
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'otherwise' do
|
29
|
+
it do
|
30
|
+
expect { grant.new }.to raise_error AttrRequired::AttrMissing
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Rack::OAuth2::Client::Grant::RefreshToken do
|
4
|
+
let(:grant) { Rack::OAuth2::Client::Grant::RefreshToken }
|
5
|
+
|
6
|
+
context 'when refresh_token is given' do
|
7
|
+
let :attributes do
|
8
|
+
{:refresh_token => 'refresh_token'}
|
9
|
+
end
|
10
|
+
subject { grant.new attributes }
|
11
|
+
its(:as_json) do
|
12
|
+
should == {:grant_type => :refresh_token, :refresh_token => 'refresh_token'}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'otherwise' do
|
17
|
+
it do
|
18
|
+
expect { grant.new }.to raise_error AttrRequired::AttrMissing
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,287 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Rack::OAuth2::Client do
|
4
|
+
let :client do
|
5
|
+
Rack::OAuth2::Client.new(
|
6
|
+
:identifier => 'client_id',
|
7
|
+
:secret => 'client_secret',
|
8
|
+
:host => 'server.example.com',
|
9
|
+
:redirect_uri => 'https://client.example.com/callback'
|
10
|
+
)
|
11
|
+
end
|
12
|
+
subject { client }
|
13
|
+
|
14
|
+
its(:identifier) { should == 'client_id' }
|
15
|
+
its(:secret) { should == 'client_secret' }
|
16
|
+
its(:authorization_endpoint) { should == '/oauth2/authorize' }
|
17
|
+
its(:token_endpoint) { should == '/oauth2/token' }
|
18
|
+
|
19
|
+
context 'when identifier is missing' do
|
20
|
+
it do
|
21
|
+
expect { Rack::OAuth2::Client.new }.to raise_error AttrRequired::AttrMissing
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#authorization_uri' do
|
26
|
+
subject { client.authorization_uri }
|
27
|
+
it { should include 'https://server.example.com/oauth2/authorize' }
|
28
|
+
it { should include 'client_id=client_id' }
|
29
|
+
it { should include 'redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback' }
|
30
|
+
it { should include 'response_type=code' }
|
31
|
+
|
32
|
+
context 'when endpoints are absolute URIs' do
|
33
|
+
before do
|
34
|
+
client.authorization_endpoint = 'https://server2.example.com/oauth/authorize'
|
35
|
+
client.token_endpoint = 'https://server2.example.com/oauth/token'
|
36
|
+
end
|
37
|
+
it { should include 'https://server2.example.com/oauth/authorize' }
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'when scheme is specified' do
|
41
|
+
before { client.scheme = 'http' }
|
42
|
+
it { should include 'http://server.example.com/oauth2/authorize' }
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when response_type is token' do
|
46
|
+
subject { client.authorization_uri(:response_type => :token) }
|
47
|
+
it { should include 'response_type=token' }
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when response_type is an Array' do
|
51
|
+
subject { client.authorization_uri(:response_type => [:token, :code]) }
|
52
|
+
it { should include 'response_type=token+code' }
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when scope is given' do
|
56
|
+
subject { client.authorization_uri(:scope => [:scope1, :scope2]) }
|
57
|
+
it { should include 'scope=scope1+scope2' }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#authorization_code=' do
|
62
|
+
before { client.authorization_code = 'code' }
|
63
|
+
subject { client.instance_variable_get('@grant') }
|
64
|
+
it { should be_instance_of Rack::OAuth2::Client::Grant::AuthorizationCode }
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#resource_owner_credentials=' do
|
68
|
+
before { client.resource_owner_credentials = 'username', 'password' }
|
69
|
+
subject { client.instance_variable_get('@grant') }
|
70
|
+
it { should be_instance_of Rack::OAuth2::Client::Grant::Password }
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#refresh_token=' do
|
74
|
+
before { client.refresh_token = 'refresh_token' }
|
75
|
+
subject { client.instance_variable_get('@grant') }
|
76
|
+
it { should be_instance_of Rack::OAuth2::Client::Grant::RefreshToken }
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#access_token!' do
|
80
|
+
subject { client.access_token! }
|
81
|
+
|
82
|
+
describe 'client authentication method' do
|
83
|
+
before do
|
84
|
+
client.authorization_code = 'code'
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should be Basic auth as default' do
|
88
|
+
mock_response(
|
89
|
+
:post,
|
90
|
+
'https://server.example.com/oauth2/token',
|
91
|
+
'tokens/bearer.json',
|
92
|
+
:request_header => {
|
93
|
+
'Authorization' => 'Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ='
|
94
|
+
}
|
95
|
+
)
|
96
|
+
client.access_token!
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'when other auth method specified' do
|
100
|
+
it do
|
101
|
+
mock_response(
|
102
|
+
:post,
|
103
|
+
'https://server.example.com/oauth2/token',
|
104
|
+
'tokens/bearer.json',
|
105
|
+
:params => {
|
106
|
+
:client_id => 'client_id',
|
107
|
+
:client_secret => 'client_secret',
|
108
|
+
:code => 'code',
|
109
|
+
:grant_type => 'authorization_code',
|
110
|
+
:redirect_uri => 'https://client.example.com/callback'
|
111
|
+
}
|
112
|
+
)
|
113
|
+
client.access_token! :client_auth_body
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'scopes' do
|
119
|
+
context 'when scope option given' do
|
120
|
+
it 'should specify given scope' do
|
121
|
+
mock_response(
|
122
|
+
:post,
|
123
|
+
'https://server.example.com/oauth2/token',
|
124
|
+
'tokens/bearer.json',
|
125
|
+
:params => {
|
126
|
+
:grant_type => 'client_credentials',
|
127
|
+
:scope => 'a b'
|
128
|
+
}
|
129
|
+
)
|
130
|
+
client.access_token! :scope => [:a, :b]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context 'when bearer token is given' do
|
136
|
+
before do
|
137
|
+
client.authorization_code = 'code'
|
138
|
+
mock_response(
|
139
|
+
:post,
|
140
|
+
'https://server.example.com/oauth2/token',
|
141
|
+
'tokens/bearer.json'
|
142
|
+
)
|
143
|
+
end
|
144
|
+
it { should be_instance_of Rack::OAuth2::AccessToken::Bearer }
|
145
|
+
its(:token_type) { should == :bearer }
|
146
|
+
its(:access_token) { should == 'access_token' }
|
147
|
+
its(:refresh_token) { should == 'refresh_token' }
|
148
|
+
its(:expires_in) { should == 3600 }
|
149
|
+
|
150
|
+
context 'when token type is "Bearer", not "bearer"' do
|
151
|
+
before do
|
152
|
+
client.authorization_code = 'code'
|
153
|
+
mock_response(
|
154
|
+
:post,
|
155
|
+
'https://server.example.com/oauth2/token',
|
156
|
+
'tokens/_Bearer.json'
|
157
|
+
)
|
158
|
+
end
|
159
|
+
it { should be_instance_of Rack::OAuth2::AccessToken::Bearer }
|
160
|
+
its(:token_type) { should == :bearer }
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context 'when mac token is given' do
|
165
|
+
before do
|
166
|
+
client.authorization_code = 'code'
|
167
|
+
mock_response(
|
168
|
+
:post,
|
169
|
+
'https://server.example.com/oauth2/token',
|
170
|
+
'tokens/mac.json'
|
171
|
+
)
|
172
|
+
end
|
173
|
+
it { should be_instance_of Rack::OAuth2::AccessToken::MAC }
|
174
|
+
its(:token_type) { should == :mac }
|
175
|
+
its(:access_token) { should == 'access_token' }
|
176
|
+
its(:refresh_token) { should == 'refresh_token' }
|
177
|
+
its(:expires_in) { should == 3600 }
|
178
|
+
end
|
179
|
+
|
180
|
+
context 'when no-type token is given (JSON)' do
|
181
|
+
before do
|
182
|
+
client.authorization_code = 'code'
|
183
|
+
mock_response(
|
184
|
+
:post,
|
185
|
+
'https://server.example.com/oauth2/token',
|
186
|
+
'tokens/legacy.json'
|
187
|
+
)
|
188
|
+
end
|
189
|
+
it { should be_instance_of Rack::OAuth2::AccessToken::Legacy }
|
190
|
+
its(:token_type) { should == :legacy }
|
191
|
+
its(:access_token) { should == 'access_token' }
|
192
|
+
its(:refresh_token) { should == 'refresh_token' }
|
193
|
+
its(:expires_in) { should == 3600 }
|
194
|
+
end
|
195
|
+
|
196
|
+
context 'when no-type token is given (key-value)' do
|
197
|
+
before do
|
198
|
+
mock_response(
|
199
|
+
:post,
|
200
|
+
'https://server.example.com/oauth2/token',
|
201
|
+
'tokens/legacy.txt'
|
202
|
+
)
|
203
|
+
end
|
204
|
+
it { should be_instance_of Rack::OAuth2::AccessToken::Legacy }
|
205
|
+
its(:token_type) { should == :legacy }
|
206
|
+
its(:access_token) { should == 'access_token' }
|
207
|
+
its(:expires_in) { should == 3600 }
|
208
|
+
|
209
|
+
context 'when expires_in is not given' do
|
210
|
+
before do
|
211
|
+
mock_response(
|
212
|
+
:post,
|
213
|
+
'https://server.example.com/oauth2/token',
|
214
|
+
'tokens/legacy_without_expires_in.txt'
|
215
|
+
)
|
216
|
+
end
|
217
|
+
its(:expires_in) { should be_nil }
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
context 'when unknown-type token is given' do
|
222
|
+
before do
|
223
|
+
client.authorization_code = 'code'
|
224
|
+
mock_response(
|
225
|
+
:post,
|
226
|
+
'https://server.example.com/oauth2/token',
|
227
|
+
'tokens/unknown.json'
|
228
|
+
)
|
229
|
+
end
|
230
|
+
it do
|
231
|
+
expect { client.access_token! }.to raise_error(StandardError, 'Unknown Token Type')
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
context 'when error response is given' do
|
236
|
+
before do
|
237
|
+
mock_response(
|
238
|
+
:post,
|
239
|
+
'https://server.example.com/oauth2/token',
|
240
|
+
'errors/invalid_request.json',
|
241
|
+
:status => 400
|
242
|
+
)
|
243
|
+
end
|
244
|
+
it do
|
245
|
+
expect { client.access_token! }.to raise_error Rack::OAuth2::Client::Error
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
context 'when no body given' do
|
250
|
+
context 'when error given' do
|
251
|
+
before do
|
252
|
+
mock_response(
|
253
|
+
:post,
|
254
|
+
'https://server.example.com/oauth2/token',
|
255
|
+
'blank',
|
256
|
+
:status => 400
|
257
|
+
)
|
258
|
+
end
|
259
|
+
it do
|
260
|
+
expect { client.access_token! }.to raise_error Rack::OAuth2::Client::Error
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
context 'when no host info' do
|
267
|
+
let :client do
|
268
|
+
Rack::OAuth2::Client.new(
|
269
|
+
:identifier => 'client_id',
|
270
|
+
:secret => 'client_secret',
|
271
|
+
:redirect_uri => 'https://client.example.com/callback'
|
272
|
+
)
|
273
|
+
end
|
274
|
+
|
275
|
+
describe '#authorization_uri' do
|
276
|
+
it do
|
277
|
+
expect { client.authorization_uri }.to raise_error 'No Host Info'
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
describe '#access_token!' do
|
282
|
+
it do
|
283
|
+
expect { client.access_token! }.to raise_error 'No Host Info'
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|