doorkeeper 3.1.0 → 4.0.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.
Potentially problematic release.
This version of doorkeeper might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +8 -10
- data/CONTRIBUTING.md +2 -0
- data/Gemfile +8 -4
- data/NEWS.md +57 -2
- data/README.md +48 -40
- data/Rakefile +1 -1
- data/app/controllers/doorkeeper/application_metal_controller.rb +1 -2
- data/app/controllers/doorkeeper/applications_controller.rb +2 -2
- data/app/controllers/doorkeeper/authorizations_controller.rb +1 -1
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -1
- data/app/controllers/doorkeeper/tokens_controller.rb +1 -1
- data/app/helpers/doorkeeper/dashboard_helper.rb +13 -11
- data/app/views/doorkeeper/applications/show.html.erb +1 -1
- data/app/views/layouts/doorkeeper/admin.html.erb +1 -1
- data/config/locales/en.yml +1 -0
- data/doorkeeper.gemspec +7 -6
- data/lib/doorkeeper/config.rb +10 -15
- data/lib/doorkeeper/engine.rb +11 -7
- data/lib/doorkeeper/helpers/controller.rb +1 -1
- data/lib/doorkeeper/models/access_grant_mixin.rb +9 -5
- data/lib/doorkeeper/models/access_token_mixin.rb +28 -22
- data/lib/doorkeeper/models/application_mixin.rb +3 -7
- data/lib/doorkeeper/models/concerns/expirable.rb +2 -2
- data/lib/doorkeeper/models/concerns/ownership.rb +6 -1
- data/lib/doorkeeper/models/concerns/revocable.rb +19 -2
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +1 -1
- data/lib/doorkeeper/oauth/client/credentials.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +2 -1
- data/lib/doorkeeper/oauth/client_credentials_request.rb +7 -4
- data/lib/doorkeeper/oauth/code_response.rb +13 -14
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +2 -1
- data/lib/doorkeeper/oauth/password_access_token_request.rb +6 -10
- data/lib/doorkeeper/oauth/refresh_token_request.rb +23 -11
- data/lib/doorkeeper/oauth/scopes.rb +2 -2
- data/lib/doorkeeper/oauth/token.rb +6 -5
- data/lib/doorkeeper/oauth/token_response.rb +1 -1
- data/lib/doorkeeper/orm/active_record/access_token.rb +8 -0
- data/lib/doorkeeper/orm/active_record/application.rb +2 -7
- data/lib/doorkeeper/orm/active_record.rb +0 -16
- data/lib/doorkeeper/rails/helpers.rb +1 -1
- data/lib/doorkeeper/rails/routes/mapper.rb +1 -1
- data/lib/doorkeeper/rails/routes.rb +2 -1
- data/lib/doorkeeper/request/password.rb +11 -1
- data/lib/doorkeeper/version.rb +1 -1
- data/lib/doorkeeper.rb +1 -1
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +29 -0
- data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb +1 -1
- data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb +11 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +2 -2
- data/lib/generators/doorkeeper/templates/migration.rb +23 -5
- data/spec/controllers/authorizations_controller_spec.rb +0 -14
- data/spec/controllers/protected_resources_controller_spec.rb +47 -18
- data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -4
- data/spec/dummy/app/controllers/home_controller.rb +1 -1
- data/spec/dummy/app/controllers/metal_controller.rb +1 -1
- data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +3 -3
- data/spec/dummy/app/models/user.rb +0 -4
- data/spec/dummy/config/application.rb +2 -36
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/dummy/config/environments/test.rb +4 -15
- data/spec/dummy/config/initializers/active_record_belongs_to_required_by_default.rb +6 -0
- data/spec/dummy/config/initializers/doorkeeper.rb +2 -2
- data/spec/dummy/db/migrate/{20130902165751_create_doorkeeper_tables.rb → 20151223192035_create_doorkeeper_tables.rb} +24 -5
- data/spec/dummy/db/migrate/{20130902175349_add_owner_to_application.rb → 20151223200000_add_owner_to_application.rb} +0 -0
- data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +11 -0
- data/spec/dummy/db/schema.rb +23 -22
- data/spec/lib/config_spec.rb +2 -2
- data/spec/lib/models/revocable_spec.rb +27 -4
- data/spec/lib/oauth/authorization_code_request_spec.rb +1 -1
- data/spec/lib/oauth/code_response_spec.rb +34 -0
- data/spec/lib/oauth/password_access_token_request_spec.rb +5 -5
- data/spec/lib/oauth/refresh_token_request_spec.rb +34 -3
- data/spec/lib/oauth/scopes_spec.rb +0 -1
- data/spec/lib/oauth/token_spec.rb +12 -5
- data/spec/models/doorkeeper/access_token_spec.rb +45 -1
- data/spec/models/doorkeeper/application_spec.rb +2 -10
- data/spec/requests/flows/password_spec.rb +26 -5
- data/spec/requests/flows/refresh_token_spec.rb +87 -17
- data/spec/spec_helper_integration.rb +3 -0
- data/spec/support/helpers/model_helper.rb +27 -5
- data/spec/support/http_method_shim.rb +24 -0
- data/spec/support/shared/controllers_shared_context.rb +13 -4
- data/spec/support/shared/models_shared_examples.rb +1 -1
- metadata +52 -32
- data/lib/generators/doorkeeper/application_scopes_generator.rb +0 -34
- data/lib/generators/doorkeeper/templates/add_scopes_to_oauth_applications.rb +0 -5
- data/spec/dummy/db/migrate/20141209001746_add_scopes_to_oauth_applications.rb +0 -5
@@ -11,20 +11,21 @@ describe 'Revocable' do
|
|
11
11
|
|
12
12
|
describe :revoke do
|
13
13
|
it 'updates :revoked_at attribute with current time' do
|
14
|
-
|
15
|
-
|
14
|
+
utc = double utc: double
|
15
|
+
clock = double now: utc
|
16
|
+
expect(subject).to receive(:update_attribute).with(:revoked_at, clock.now.utc)
|
16
17
|
subject.revoke(clock)
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
20
21
|
describe :revoked? do
|
21
22
|
it 'is revoked if :revoked_at has passed' do
|
22
|
-
allow(subject).to receive(:revoked_at).and_return(Time.now - 1000)
|
23
|
+
allow(subject).to receive(:revoked_at).and_return(Time.now.utc - 1000)
|
23
24
|
expect(subject).to be_revoked
|
24
25
|
end
|
25
26
|
|
26
27
|
it 'is not revoked if :revoked_at has not passed' do
|
27
|
-
allow(subject).to receive(:revoked_at).and_return(Time.now + 1000)
|
28
|
+
allow(subject).to receive(:revoked_at).and_return(Time.now.utc + 1000)
|
28
29
|
expect(subject).not_to be_revoked
|
29
30
|
end
|
30
31
|
|
@@ -33,4 +34,26 @@ describe 'Revocable' do
|
|
33
34
|
expect(subject).not_to be_revoked
|
34
35
|
end
|
35
36
|
end
|
37
|
+
|
38
|
+
describe :revoke_previous_refresh_token! do
|
39
|
+
it "revokes the previous token if existing, and resets the
|
40
|
+
`previous_refresh_token` attribute" do
|
41
|
+
previous_token = FactoryGirl.create(
|
42
|
+
:access_token,
|
43
|
+
refresh_token: "refresh_token"
|
44
|
+
)
|
45
|
+
current_token = FactoryGirl.create(
|
46
|
+
:access_token,
|
47
|
+
previous_refresh_token: previous_token.refresh_token
|
48
|
+
)
|
49
|
+
|
50
|
+
expect_any_instance_of(
|
51
|
+
Doorkeeper::AccessToken
|
52
|
+
).to receive(:revoke).and_call_original
|
53
|
+
current_token.revoke_previous_refresh_token!
|
54
|
+
|
55
|
+
expect(current_token.previous_refresh_token).to be_empty
|
56
|
+
expect(previous_token.reload).to be_revoked
|
57
|
+
end
|
58
|
+
end
|
36
59
|
end
|
@@ -18,7 +18,7 @@ module Doorkeeper::OAuth
|
|
18
18
|
it 'issues a new token for the client' do
|
19
19
|
expect do
|
20
20
|
subject.authorize
|
21
|
-
end.to change { client.access_tokens.count }.by(1)
|
21
|
+
end.to change { client.reload.access_tokens.count }.by(1)
|
22
22
|
end
|
23
23
|
|
24
24
|
it "issues the token with same grant's scopes" do
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Doorkeeper
|
4
|
+
module OAuth
|
5
|
+
describe CodeResponse do
|
6
|
+
describe '.redirect_uri' do
|
7
|
+
context 'when generating the redirect URI for an implicit grant' do
|
8
|
+
let :pre_auth do
|
9
|
+
double(
|
10
|
+
:pre_auth,
|
11
|
+
client: double(:application, id: 1),
|
12
|
+
redirect_uri: 'http://tst.com/cb',
|
13
|
+
state: nil,
|
14
|
+
scopes: Scopes.from_string('public'),
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
let :auth do
|
19
|
+
Authorization::Token.new(pre_auth, double(id: 1)).tap do |c|
|
20
|
+
c.issue_token
|
21
|
+
allow(c.token).to receive(:expires_in_seconds).and_return(3600)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
subject { CodeResponse.new(pre_auth, auth, response_on_fragment: true).redirect_uri }
|
26
|
+
|
27
|
+
it 'includes the remaining TTL of the token relative to the time the token was generated' do
|
28
|
+
expect(subject).to include('expires_in=3600')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -11,23 +11,22 @@ module Doorkeeper::OAuth
|
|
11
11
|
custom_access_token_expires_in: ->(_app) { nil }
|
12
12
|
)
|
13
13
|
end
|
14
|
-
let(:credentials) { Client::Credentials.new(client.uid, client.secret) }
|
15
14
|
let(:client) { FactoryGirl.create(:application) }
|
16
15
|
let(:owner) { double :owner, id: 99 }
|
17
16
|
|
18
17
|
subject do
|
19
|
-
PasswordAccessTokenRequest.new(server,
|
18
|
+
PasswordAccessTokenRequest.new(server, client, owner)
|
20
19
|
end
|
21
20
|
|
22
21
|
it 'issues a new token for the client' do
|
23
22
|
expect do
|
24
23
|
subject.authorize
|
25
|
-
end.to change { client.access_tokens.count }.by(1)
|
24
|
+
end.to change { client.reload.access_tokens.count }.by(1)
|
26
25
|
end
|
27
26
|
|
28
27
|
it 'issues a new token without a client' do
|
29
28
|
expect do
|
30
|
-
subject.
|
29
|
+
subject.client = nil
|
31
30
|
subject.authorize
|
32
31
|
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
33
32
|
end
|
@@ -35,6 +34,7 @@ module Doorkeeper::OAuth
|
|
35
34
|
it 'does not issue a new token with an invalid client' do
|
36
35
|
expect do
|
37
36
|
subject.client = nil
|
37
|
+
subject.parameters = { client_id: 'bad_id' }
|
38
38
|
subject.authorize
|
39
39
|
end.to_not change { Doorkeeper::AccessToken.count }
|
40
40
|
|
@@ -48,7 +48,7 @@ module Doorkeeper::OAuth
|
|
48
48
|
end
|
49
49
|
|
50
50
|
it 'optionally accepts the client' do
|
51
|
-
subject.
|
51
|
+
subject.client = nil
|
52
52
|
expect(subject).to be_valid
|
53
53
|
end
|
54
54
|
|
@@ -2,6 +2,9 @@ require 'spec_helper_integration'
|
|
2
2
|
|
3
3
|
module Doorkeeper::OAuth
|
4
4
|
describe RefreshTokenRequest do
|
5
|
+
before do
|
6
|
+
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
|
7
|
+
end
|
5
8
|
let(:server) do
|
6
9
|
double :server,
|
7
10
|
access_token_expires_in: 2.minutes,
|
@@ -16,9 +19,7 @@ module Doorkeeper::OAuth
|
|
16
19
|
subject { RefreshTokenRequest.new server, refresh_token, credentials }
|
17
20
|
|
18
21
|
it 'issues a new token for the client' do
|
19
|
-
expect
|
20
|
-
subject.authorize
|
21
|
-
end.to change { client.access_tokens.count }.by(1)
|
22
|
+
expect { subject.authorize }.to change { client.reload.access_tokens.count }.by(1)
|
22
23
|
expect(client.reload.access_tokens.last.expires_in).to eq(120)
|
23
24
|
end
|
24
25
|
|
@@ -27,6 +28,8 @@ module Doorkeeper::OAuth
|
|
27
28
|
access_token_expires_in: 2.minutes,
|
28
29
|
custom_access_token_expires_in: ->(_oauth_client) { 1234 }
|
29
30
|
|
31
|
+
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
|
32
|
+
|
30
33
|
RefreshTokenRequest.new(server, refresh_token, credentials).authorize
|
31
34
|
|
32
35
|
expect(client.reload.access_tokens.last.expires_in).to eq(1234)
|
@@ -67,6 +70,34 @@ module Doorkeeper::OAuth
|
|
67
70
|
expect(subject).to be_valid
|
68
71
|
end
|
69
72
|
|
73
|
+
context 'refresh tokens expire on access token use' do
|
74
|
+
let(:server) do
|
75
|
+
double :server,
|
76
|
+
access_token_expires_in: 2.minutes,
|
77
|
+
custom_access_token_expires_in: ->(_oauth_client) { 1234 }
|
78
|
+
end
|
79
|
+
|
80
|
+
before do
|
81
|
+
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(true)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'issues a new token for the client' do
|
85
|
+
expect { subject.authorize }.to change { client.reload.access_tokens.count }.by(1)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'does not revoke the previous token' do
|
89
|
+
subject.authorize
|
90
|
+
expect(refresh_token).not_to be_revoked
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'sets the previous refresh token in the new access token' do
|
94
|
+
subject.authorize
|
95
|
+
expect(
|
96
|
+
client.access_tokens.last.previous_refresh_token
|
97
|
+
).to eq(refresh_token.refresh_token)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
70
101
|
context 'clientless access tokens' do
|
71
102
|
let!(:refresh_token) { FactoryGirl.create(:clientless_access_token, use_refresh_token: true) }
|
72
103
|
|
@@ -30,7 +30,7 @@ module Doorkeeper
|
|
30
30
|
it 'stops at the first credentials found' do
|
31
31
|
not_called_method = double
|
32
32
|
expect(not_called_method).not_to receive(:call)
|
33
|
-
Token.from_request request, ->(
|
33
|
+
Token.from_request request, ->(_r) {}, method, not_called_method
|
34
34
|
end
|
35
35
|
|
36
36
|
it 'returns the credential from extractor method' do
|
@@ -96,13 +96,20 @@ module Doorkeeper
|
|
96
96
|
end
|
97
97
|
|
98
98
|
describe :authenticate do
|
99
|
-
|
100
|
-
|
101
|
-
it 'calls the finder if token was found' do
|
102
|
-
token = ->(r) { 'token' }
|
99
|
+
it 'calls the finder if token was returned' do
|
100
|
+
token = ->(_r) { 'token' }
|
103
101
|
expect(AccessToken).to receive(:by_token).with('token')
|
104
102
|
Token.authenticate double, token
|
105
103
|
end
|
104
|
+
|
105
|
+
it 'revokes previous refresh_token if token was found' do
|
106
|
+
token = ->(_r) { 'token' }
|
107
|
+
expect(
|
108
|
+
AccessToken
|
109
|
+
).to receive(:by_token).with('token').and_return(token)
|
110
|
+
expect(token).to receive(:revoke_previous_refresh_token!)
|
111
|
+
Token.authenticate double, token
|
112
|
+
end
|
106
113
|
end
|
107
114
|
end
|
108
115
|
end
|
@@ -12,6 +12,11 @@ module Doorkeeper
|
|
12
12
|
let(:factory_name) { :access_token }
|
13
13
|
end
|
14
14
|
|
15
|
+
module CustomGeneratorArgs
|
16
|
+
def self.generate
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
15
20
|
describe :generate_token do
|
16
21
|
it 'generates a token using the default method' do
|
17
22
|
FactoryGirl.create :access_token
|
@@ -21,6 +26,10 @@ module Doorkeeper
|
|
21
26
|
end
|
22
27
|
|
23
28
|
it 'generates a token using a custom object' do
|
29
|
+
eigenclass = class << CustomGeneratorArgs; self; end
|
30
|
+
eigenclass.class_eval do
|
31
|
+
remove_method :generate
|
32
|
+
end
|
24
33
|
module CustomGeneratorArgs
|
25
34
|
def self.generate(opts = {})
|
26
35
|
"custom_generator_token_#{opts[:resource_owner_id]}"
|
@@ -37,6 +46,10 @@ module Doorkeeper
|
|
37
46
|
end
|
38
47
|
|
39
48
|
it 'allows the custom generator to access the application details' do
|
49
|
+
eigenclass = class << CustomGeneratorArgs; self; end
|
50
|
+
eigenclass.class_eval do
|
51
|
+
remove_method :generate
|
52
|
+
end
|
40
53
|
module CustomGeneratorArgs
|
41
54
|
def self.generate(opts = {})
|
42
55
|
"custom_generator_token_#{opts[:application].name}"
|
@@ -53,6 +66,10 @@ module Doorkeeper
|
|
53
66
|
end
|
54
67
|
|
55
68
|
it 'allows the custom generator to access the scopes' do
|
69
|
+
eigenclass = class << CustomGeneratorArgs; self; end
|
70
|
+
eigenclass.class_eval do
|
71
|
+
remove_method :generate
|
72
|
+
end
|
56
73
|
module CustomGeneratorArgs
|
57
74
|
def self.generate(opts = {})
|
58
75
|
"custom_generator_token_#{opts[:scopes].count}_#{opts[:scopes]}"
|
@@ -70,6 +87,10 @@ module Doorkeeper
|
|
70
87
|
end
|
71
88
|
|
72
89
|
it 'allows the custom generator to access the expiry length' do
|
90
|
+
eigenclass = class << CustomGeneratorArgs; self; end
|
91
|
+
eigenclass.class_eval do
|
92
|
+
remove_method :generate
|
93
|
+
end
|
73
94
|
module CustomGeneratorArgs
|
74
95
|
def self.generate(opts = {})
|
75
96
|
"custom_generator_token_#{opts[:expires_in]}"
|
@@ -85,6 +106,23 @@ module Doorkeeper
|
|
85
106
|
expect(token.token).to eq 'custom_generator_token_7200'
|
86
107
|
end
|
87
108
|
|
109
|
+
it 'allows the custom generator to access the created time' do
|
110
|
+
module CustomGeneratorArgs
|
111
|
+
def self.generate(opts = {})
|
112
|
+
"custom_generator_token_#{opts[:created_at].to_i}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
Doorkeeper.configure do
|
117
|
+
orm DOORKEEPER_ORM
|
118
|
+
access_token_generator "Doorkeeper::CustomGeneratorArgs"
|
119
|
+
end
|
120
|
+
|
121
|
+
token = FactoryGirl.create :access_token
|
122
|
+
created_at = token.created_at
|
123
|
+
expect(token.token).to eq "custom_generator_token_#{created_at.to_i}"
|
124
|
+
end
|
125
|
+
|
88
126
|
it 'raises an error if the custom object does not support generate' do
|
89
127
|
module NoGenerate
|
90
128
|
end
|
@@ -133,7 +171,7 @@ module Doorkeeper
|
|
133
171
|
expect do
|
134
172
|
token2.refresh_token = token1.refresh_token
|
135
173
|
token2.save(validate: false)
|
136
|
-
end.to raise_error(
|
174
|
+
end.to raise_error(uniqueness_error)
|
137
175
|
end
|
138
176
|
end
|
139
177
|
|
@@ -143,6 +181,12 @@ module Doorkeeper
|
|
143
181
|
subject.resource_owner_id = nil
|
144
182
|
expect(subject).to be_valid
|
145
183
|
end
|
184
|
+
|
185
|
+
it 'is valid without application_id' do
|
186
|
+
# For resource owner credentials flow
|
187
|
+
subject.application_id = nil
|
188
|
+
expect(subject).to be_valid
|
189
|
+
end
|
146
190
|
end
|
147
191
|
|
148
192
|
describe '#same_credential?' do
|
@@ -90,7 +90,7 @@ module Doorkeeper
|
|
90
90
|
app1 = FactoryGirl.create(:application)
|
91
91
|
app2 = FactoryGirl.create(:application)
|
92
92
|
app2.uid = app1.uid
|
93
|
-
expect { app2.save!(validate: false) }.to raise_error(
|
93
|
+
expect { app2.save!(validate: false) }.to raise_error(uniqueness_error)
|
94
94
|
end
|
95
95
|
|
96
96
|
it 'generate secret on create' do
|
@@ -129,7 +129,7 @@ module Doorkeeper
|
|
129
129
|
|
130
130
|
it 'should destroy its access tokens' do
|
131
131
|
FactoryGirl.create(:access_token, application: new_application)
|
132
|
-
FactoryGirl.create(:access_token, application: new_application, revoked_at: Time.now)
|
132
|
+
FactoryGirl.create(:access_token, application: new_application, revoked_at: Time.now.utc)
|
133
133
|
expect do
|
134
134
|
new_application.destroy
|
135
135
|
end.to change { Doorkeeper::AccessToken.count }.by(-2)
|
@@ -166,14 +166,6 @@ module Doorkeeper
|
|
166
166
|
FactoryGirl.create(:access_token, resource_owner_id: resource_owner.id, application: application)
|
167
167
|
expect(Application.authorized_for(resource_owner)).to eq([application])
|
168
168
|
end
|
169
|
-
|
170
|
-
it 'should fail to mass assign a new application', if: ::Rails::VERSION::MAJOR < 4 do
|
171
|
-
mass_assign = { name: 'Something',
|
172
|
-
redirect_uri: 'http://somewhere.com/something',
|
173
|
-
uid: 123,
|
174
|
-
secret: 'something' }
|
175
|
-
expect(Application.create(mass_assign).uid).not_to eq(123)
|
176
|
-
end
|
177
169
|
end
|
178
170
|
|
179
171
|
describe :authenticate do
|
@@ -24,14 +24,26 @@ describe 'Resource Owner Password Credentials Flow' do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
context 'with valid user credentials' do
|
27
|
-
it 'should issue new token' do
|
27
|
+
it 'should issue new token with confidential client' do
|
28
28
|
expect do
|
29
29
|
post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)
|
30
30
|
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
31
31
|
|
32
32
|
token = Doorkeeper::AccessToken.first
|
33
33
|
|
34
|
-
|
34
|
+
expect(token.application_id).to eq @client.id
|
35
|
+
should_have_json 'access_token', token.token
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should issue new token with public client (only client_id present)' do
|
39
|
+
expect do
|
40
|
+
post password_token_endpoint_url(client_id: @client.uid, resource_owner: @resource_owner)
|
41
|
+
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
42
|
+
|
43
|
+
token = Doorkeeper::AccessToken.first
|
44
|
+
|
45
|
+
expect(token.application_id).to eq @client.id
|
46
|
+
should_have_json 'access_token', token.token
|
35
47
|
end
|
36
48
|
|
37
49
|
it 'should issue new token without client credentials' do
|
@@ -41,7 +53,8 @@ describe 'Resource Owner Password Credentials Flow' do
|
|
41
53
|
|
42
54
|
token = Doorkeeper::AccessToken.first
|
43
55
|
|
44
|
-
|
56
|
+
expect(token.application_id).to be_nil
|
57
|
+
should_have_json 'access_token', token.token
|
45
58
|
end
|
46
59
|
|
47
60
|
it 'should issue a refresh token if enabled' do
|
@@ -51,7 +64,7 @@ describe 'Resource Owner Password Credentials Flow' do
|
|
51
64
|
|
52
65
|
token = Doorkeeper::AccessToken.first
|
53
66
|
|
54
|
-
should_have_json 'refresh_token',
|
67
|
+
should_have_json 'refresh_token', token.refresh_token
|
55
68
|
end
|
56
69
|
|
57
70
|
it 'should return the same token if it is still accessible' do
|
@@ -82,7 +95,7 @@ describe 'Resource Owner Password Credentials Flow' do
|
|
82
95
|
end
|
83
96
|
end
|
84
97
|
|
85
|
-
context 'with invalid client credentials' do
|
98
|
+
context 'with invalid confidential client credentials' do
|
86
99
|
it 'should not issue new token with bad client credentials' do
|
87
100
|
expect do
|
88
101
|
post password_token_endpoint_url(client_id: @client.uid,
|
@@ -91,4 +104,12 @@ describe 'Resource Owner Password Credentials Flow' do
|
|
91
104
|
end.to_not change { Doorkeeper::AccessToken.count }
|
92
105
|
end
|
93
106
|
end
|
107
|
+
|
108
|
+
context 'with invalid public client id' do
|
109
|
+
it 'should not issue new token with bad client id' do
|
110
|
+
expect do
|
111
|
+
post password_token_endpoint_url(client_id: 'bad_id', resource_owner: @resource_owner)
|
112
|
+
end.to_not change { Doorkeeper::AccessToken.count }
|
113
|
+
end
|
114
|
+
end
|
94
115
|
end
|
@@ -37,20 +37,62 @@ describe 'Refresh Token Flow' do
|
|
37
37
|
|
38
38
|
context 'refreshing the token' do
|
39
39
|
before do
|
40
|
-
@token = FactoryGirl.create(
|
40
|
+
@token = FactoryGirl.create(
|
41
|
+
:access_token,
|
42
|
+
application: @client,
|
43
|
+
resource_owner_id: 1,
|
44
|
+
use_refresh_token: true
|
45
|
+
)
|
41
46
|
end
|
42
47
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
48
|
+
context "refresh_token revoked on use" do
|
49
|
+
it 'client request a token with refresh token' do
|
50
|
+
post refresh_token_endpoint_url(
|
51
|
+
client: @client, refresh_token: @token.refresh_token
|
52
|
+
)
|
53
|
+
should_have_json(
|
54
|
+
'refresh_token', Doorkeeper::AccessToken.last.refresh_token
|
55
|
+
)
|
56
|
+
expect(@token.reload).not_to be_revoked
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'client request a token with expired access token' do
|
60
|
+
@token.update_attribute :expires_in, -100
|
61
|
+
post refresh_token_endpoint_url(
|
62
|
+
client: @client, refresh_token: @token.refresh_token
|
63
|
+
)
|
64
|
+
should_have_json(
|
65
|
+
'refresh_token', Doorkeeper::AccessToken.last.refresh_token
|
66
|
+
)
|
67
|
+
expect(@token.reload).not_to be_revoked
|
68
|
+
end
|
47
69
|
end
|
48
70
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
71
|
+
context "refresh_token revoked on refresh_token request" do
|
72
|
+
before do
|
73
|
+
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'client request a token with refresh token' do
|
77
|
+
post refresh_token_endpoint_url(
|
78
|
+
client: @client, refresh_token: @token.refresh_token
|
79
|
+
)
|
80
|
+
should_have_json(
|
81
|
+
'refresh_token', Doorkeeper::AccessToken.last.refresh_token
|
82
|
+
)
|
83
|
+
expect(@token.reload).to be_revoked
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'client request a token with expired access token' do
|
87
|
+
@token.update_attribute :expires_in, -100
|
88
|
+
post refresh_token_endpoint_url(
|
89
|
+
client: @client, refresh_token: @token.refresh_token
|
90
|
+
)
|
91
|
+
should_have_json(
|
92
|
+
'refresh_token', Doorkeeper::AccessToken.last.refresh_token
|
93
|
+
)
|
94
|
+
expect(@token.reload).to be_revoked
|
95
|
+
end
|
54
96
|
end
|
55
97
|
|
56
98
|
it 'client gets an error for invalid refresh token' do
|
@@ -79,20 +121,48 @@ describe 'Refresh Token Flow' do
|
|
79
121
|
before do
|
80
122
|
# enable password auth to simulate other devices
|
81
123
|
config_is_set(:grant_flows, ["password"])
|
82
|
-
config_is_set(:resource_owner_from_credentials)
|
124
|
+
config_is_set(:resource_owner_from_credentials) do
|
125
|
+
User.authenticate! params[:username], params[:password]
|
126
|
+
end
|
83
127
|
create_resource_owner
|
84
|
-
_another_token = post password_token_endpoint_url(
|
128
|
+
_another_token = post password_token_endpoint_url(
|
129
|
+
client: @client, resource_owner: @resource_owner
|
130
|
+
)
|
85
131
|
last_token.update_attribute :created_at, 5.seconds.ago
|
86
132
|
|
87
|
-
@token = FactoryGirl.create(
|
133
|
+
@token = FactoryGirl.create(
|
134
|
+
:access_token,
|
135
|
+
application: @client,
|
136
|
+
resource_owner_id: @resource_owner.id,
|
137
|
+
use_refresh_token: true
|
138
|
+
)
|
88
139
|
@token.update_attribute :expires_in, -100
|
89
140
|
end
|
90
141
|
|
91
|
-
|
92
|
-
|
142
|
+
context "refresh_token revoked on use" do
|
143
|
+
it 'client request a token after creating another token with the same user' do
|
144
|
+
post refresh_token_endpoint_url(
|
145
|
+
client: @client, refresh_token: @token.refresh_token
|
146
|
+
)
|
147
|
+
|
148
|
+
should_have_json 'refresh_token', last_token.refresh_token
|
149
|
+
expect(@token.reload).not_to be_revoked
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "refresh_token revoked on refresh_token request" do
|
154
|
+
before do
|
155
|
+
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'client request a token after creating another token with the same user' do
|
159
|
+
post refresh_token_endpoint_url(
|
160
|
+
client: @client, refresh_token: @token.refresh_token
|
161
|
+
)
|
93
162
|
|
94
|
-
|
95
|
-
|
163
|
+
should_have_json 'refresh_token', last_token.refresh_token
|
164
|
+
expect(@token.reload).to be_revoked
|
165
|
+
end
|
96
166
|
end
|
97
167
|
|
98
168
|
def last_token
|
@@ -35,6 +35,9 @@ ENGINE_RAILS_ROOT = File.join(File.dirname(__FILE__), '../')
|
|
35
35
|
|
36
36
|
Dir["#{File.dirname(__FILE__)}/support/{dependencies,helpers,shared}/*.rb"].each { |f| require f }
|
37
37
|
|
38
|
+
# Remove after dropping support of Rails 4.2
|
39
|
+
require "#{File.dirname(__FILE__)}/support/http_method_shim.rb"
|
40
|
+
|
38
41
|
RSpec.configure do |config|
|
39
42
|
config.infer_spec_type_from_file_location!
|
40
43
|
config.mock_with :rspec
|
@@ -13,14 +13,20 @@ module ModelHelper
|
|
13
13
|
|
14
14
|
def access_grant_should_exist_for(client, resource_owner)
|
15
15
|
grant = Doorkeeper::AccessGrant.first
|
16
|
-
|
17
|
-
grant.
|
16
|
+
|
17
|
+
expect(grant.application).to have_attributes(id: client.id).
|
18
|
+
and(be_instance_of(Doorkeeper::Application))
|
19
|
+
|
20
|
+
expect(grant.resource_owner_id).to eq(resource_owner.id)
|
18
21
|
end
|
19
22
|
|
20
23
|
def access_token_should_exist_for(client, resource_owner)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
+
token = Doorkeeper::AccessToken.first
|
25
|
+
|
26
|
+
expect(token.application).to have_attributes(id: client.id).
|
27
|
+
and(be_instance_of(Doorkeeper::Application))
|
28
|
+
|
29
|
+
expect(token.resource_owner_id).to eq(resource_owner.id)
|
24
30
|
end
|
25
31
|
|
26
32
|
def access_grant_should_not_exist
|
@@ -40,6 +46,22 @@ module ModelHelper
|
|
40
46
|
grant = Doorkeeper::AccessToken.last
|
41
47
|
expect(grant.scopes).to eq(Doorkeeper::OAuth::Scopes.from_array(args))
|
42
48
|
end
|
49
|
+
|
50
|
+
def uniqueness_error
|
51
|
+
case DOORKEEPER_ORM
|
52
|
+
when :active_record
|
53
|
+
ActiveRecord::RecordNotUnique
|
54
|
+
when :sequel
|
55
|
+
error_classes = [Sequel::UniqueConstraintViolation, Sequel::ValidationFailed]
|
56
|
+
proc { |error| expect(error.class).to be_in(error_classes) }
|
57
|
+
when :mongo_mapper
|
58
|
+
MongoMapper::DocumentNotValid
|
59
|
+
when /mongoid/
|
60
|
+
Mongoid::Errors::Validations
|
61
|
+
else
|
62
|
+
raise "'#{DOORKEEPER_ORM}' ORM is not supported!"
|
63
|
+
end
|
64
|
+
end
|
43
65
|
end
|
44
66
|
|
45
67
|
RSpec.configuration.send :include, ModelHelper
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Rails 5 deprecates calling HTTP action methods with positional arguments
|
2
|
+
# in favor of keyword arguments. However, the keyword argument form is only
|
3
|
+
# supported in Rails 5+. Since we support back to 4, we need some sort of shim
|
4
|
+
# to avoid super noisy deprecations when running tests.
|
5
|
+
module HTTPMethodShim
|
6
|
+
def get(path, params = nil, headers = nil)
|
7
|
+
super(path, params: params, headers: headers)
|
8
|
+
end
|
9
|
+
|
10
|
+
def post(path, params = nil, headers = nil)
|
11
|
+
super(path, params: params, headers: headers)
|
12
|
+
end
|
13
|
+
|
14
|
+
def put(path, params = nil, headers = nil)
|
15
|
+
super(path, params: params, headers: headers)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
if ::Rails::VERSION::MAJOR >= 5
|
20
|
+
RSpec.configure do |config|
|
21
|
+
config.include HTTPMethodShim, type: :controller
|
22
|
+
config.include HTTPMethodShim, type: :request
|
23
|
+
end
|
24
|
+
end
|