doorkeeper 3.1.0 → 4.4.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of doorkeeper might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.github/ISSUE_TEMPLATE.md +25 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +17 -0
- data/.gitignore +6 -1
- data/.hound.yml +2 -13
- data/.rubocop.yml +17 -0
- data/.travis.yml +26 -10
- data/Appraisals +18 -0
- data/CODE_OF_CONDUCT.md +46 -0
- data/CONTRIBUTING.md +2 -0
- data/Gemfile +5 -5
- data/NEWS.md +141 -2
- data/README.md +149 -66
- data/RELEASING.md +5 -12
- data/Rakefile +1 -1
- data/SECURITY.md +15 -0
- data/app/controllers/doorkeeper/application_controller.rb +4 -6
- data/app/controllers/doorkeeper/application_metal_controller.rb +3 -2
- data/app/controllers/doorkeeper/applications_controller.rb +18 -8
- 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 +62 -15
- data/app/helpers/doorkeeper/dashboard_helper.rb +14 -10
- data/app/validators/redirect_uri_validator.rb +12 -2
- data/app/views/doorkeeper/applications/_delete_form.html.erb +1 -2
- data/app/views/doorkeeper/applications/_form.html.erb +13 -2
- data/app/views/doorkeeper/applications/index.html.erb +2 -0
- data/app/views/doorkeeper/applications/show.html.erb +4 -1
- data/app/views/doorkeeper/authorizations/new.html.erb +1 -1
- data/app/views/doorkeeper/authorized_applications/_delete_form.html.erb +1 -2
- data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
- data/app/views/layouts/doorkeeper/admin.html.erb +1 -1
- data/config/locales/en.yml +12 -7
- data/doorkeeper.gemspec +16 -11
- data/gemfiles/rails_4_2.gemfile +13 -0
- data/gemfiles/rails_5_0.gemfile +12 -0
- data/gemfiles/rails_5_1.gemfile +12 -0
- data/gemfiles/rails_5_2.gemfile +12 -0
- data/gemfiles/rails_master.gemfile +14 -0
- data/lib/doorkeeper/config.rb +119 -46
- data/lib/doorkeeper/engine.rb +11 -7
- data/lib/doorkeeper/errors.rb +18 -0
- data/lib/doorkeeper/grape/helpers.rb +14 -8
- data/lib/doorkeeper/helpers/controller.rb +8 -19
- data/lib/doorkeeper/models/access_grant_mixin.rb +10 -21
- data/lib/doorkeeper/models/access_token_mixin.rb +147 -43
- data/lib/doorkeeper/models/application_mixin.rb +33 -35
- data/lib/doorkeeper/models/concerns/accessible.rb +4 -0
- data/lib/doorkeeper/models/concerns/expirable.rb +15 -5
- data/lib/doorkeeper/models/concerns/orderable.rb +13 -0
- data/lib/doorkeeper/models/concerns/ownership.rb +6 -1
- data/lib/doorkeeper/models/concerns/revocable.rb +37 -2
- data/lib/doorkeeper/oauth/authorization/token.rb +22 -18
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +20 -18
- data/lib/doorkeeper/oauth/authorization_code_request.rb +7 -5
- data/lib/doorkeeper/oauth/{request_concern.rb → base_request.rb} +9 -2
- data/lib/doorkeeper/oauth/base_response.rb +29 -0
- data/lib/doorkeeper/oauth/client/credentials.rb +21 -8
- data/lib/doorkeeper/oauth/client.rb +2 -3
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +3 -2
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials_request.rb +8 -8
- data/lib/doorkeeper/oauth/code_response.rb +16 -16
- data/lib/doorkeeper/oauth/error.rb +2 -2
- data/lib/doorkeeper/oauth/error_response.rb +10 -10
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +17 -1
- data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -4
- data/lib/doorkeeper/oauth/password_access_token_request.rb +8 -13
- data/lib/doorkeeper/oauth/pre_authorization.rb +5 -3
- data/lib/doorkeeper/oauth/refresh_token_request.rb +23 -14
- data/lib/doorkeeper/oauth/scopes.rb +18 -8
- data/lib/doorkeeper/oauth/token.rb +20 -21
- data/lib/doorkeeper/oauth/token_introspection.rb +128 -0
- data/lib/doorkeeper/oauth/token_request.rb +1 -2
- data/lib/doorkeeper/oauth/token_response.rb +1 -1
- data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -0
- data/lib/doorkeeper/orm/active_record/access_token.rb +34 -8
- data/lib/doorkeeper/orm/active_record/application.rb +48 -11
- data/lib/doorkeeper/orm/active_record.rb +17 -22
- data/lib/doorkeeper/rails/helpers.rb +6 -9
- data/lib/doorkeeper/rails/routes/mapper.rb +4 -4
- data/lib/doorkeeper/rails/routes/mapping.rb +1 -1
- data/lib/doorkeeper/rails/routes.rb +17 -11
- data/lib/doorkeeper/request/authorization_code.rb +7 -1
- data/lib/doorkeeper/request/password.rb +2 -2
- data/lib/doorkeeper/request/refresh_token.rb +1 -1
- data/lib/doorkeeper/request.rb +7 -1
- data/lib/doorkeeper/server.rb +0 -8
- data/lib/doorkeeper/validations.rb +3 -2
- data/lib/doorkeeper/version.rb +34 -1
- data/lib/doorkeeper.rb +10 -2
- data/lib/generators/doorkeeper/add_client_confidentiality_generator.rb +31 -0
- data/lib/generators/doorkeeper/application_owner_generator.rb +11 -2
- data/lib/generators/doorkeeper/migration_generator.rb +13 -1
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +35 -0
- data/lib/generators/doorkeeper/templates/add_confidential_to_application_migration.rb.erb +11 -0
- data/{spec/dummy/db/migrate/20130902175349_add_owner_to_application.rb → lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb} +1 -1
- data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb +11 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +38 -6
- data/lib/generators/doorkeeper/templates/migration.rb.erb +69 -0
- data/spec/controllers/application_metal_controller.rb +10 -0
- data/spec/controllers/applications_controller_spec.rb +15 -4
- data/spec/controllers/authorizations_controller_spec.rb +74 -27
- data/spec/controllers/protected_resources_controller_spec.rb +70 -32
- data/spec/controllers/token_info_controller_spec.rb +17 -13
- data/spec/controllers/tokens_controller_spec.rb +198 -12
- 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/doorkeeper.rb +19 -3
- data/spec/dummy/config/initializers/new_framework_defaults.rb +6 -0
- data/spec/dummy/config/initializers/secret_token.rb +0 -1
- data/spec/dummy/db/migrate/20111122132257_create_users.rb +3 -1
- data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +3 -1
- data/{lib/generators/doorkeeper/templates/migration.rb → spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb} +16 -4
- data/{lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb → spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb} +4 -2
- data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +13 -0
- data/spec/dummy/db/migrate/20180210183654_add_confidential_to_application.rb +13 -0
- data/spec/dummy/db/schema.rb +24 -22
- data/spec/factories.rb +4 -2
- data/spec/generators/application_owner_generator_spec.rb +24 -5
- data/spec/generators/migration_generator_spec.rb +24 -3
- data/spec/generators/previous_refresh_token_generator_spec.rb +57 -0
- data/spec/grape/grape_integration_spec.rb +135 -0
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
- data/spec/lib/config_spec.rb +159 -14
- data/spec/lib/doorkeeper_spec.rb +135 -13
- data/spec/lib/models/expirable_spec.rb +0 -1
- data/spec/lib/models/revocable_spec.rb +27 -4
- data/spec/lib/oauth/authorization/uri_builder_spec.rb +1 -2
- data/spec/lib/oauth/authorization_code_request_spec.rb +55 -12
- data/spec/lib/oauth/base_request_spec.rb +155 -0
- data/spec/lib/oauth/base_response_spec.rb +45 -0
- data/spec/lib/oauth/client/credentials_spec.rb +45 -2
- data/spec/lib/oauth/client_credentials/creator_spec.rb +1 -1
- data/spec/lib/oauth/client_credentials_integration_spec.rb +1 -1
- data/spec/lib/oauth/client_credentials_request_spec.rb +1 -0
- data/spec/lib/oauth/code_request_spec.rb +1 -3
- data/spec/lib/oauth/code_response_spec.rb +34 -0
- data/spec/lib/oauth/error_response_spec.rb +9 -9
- data/spec/lib/oauth/error_spec.rb +1 -1
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +115 -1
- data/spec/lib/oauth/invalid_token_response_spec.rb +36 -8
- data/spec/lib/oauth/password_access_token_request_spec.rb +14 -8
- data/spec/lib/oauth/pre_authorization_spec.rb +12 -7
- data/spec/lib/oauth/refresh_token_request_spec.rb +52 -9
- data/spec/lib/oauth/scopes_spec.rb +28 -2
- data/spec/lib/oauth/token_request_spec.rb +6 -8
- data/spec/lib/oauth/token_spec.rb +12 -5
- data/spec/lib/server_spec.rb +10 -3
- data/spec/models/doorkeeper/access_grant_spec.rb +1 -1
- data/spec/models/doorkeeper/access_token_spec.rb +116 -48
- data/spec/models/doorkeeper/application_spec.rb +145 -29
- data/spec/requests/applications/applications_request_spec.rb +5 -5
- data/spec/requests/endpoints/authorization_spec.rb +5 -6
- data/spec/requests/endpoints/token_spec.rb +8 -1
- data/spec/requests/flows/authorization_code_errors_spec.rb +11 -1
- data/spec/requests/flows/authorization_code_spec.rb +6 -13
- data/spec/requests/flows/client_credentials_spec.rb +29 -1
- data/spec/requests/flows/implicit_grant_errors_spec.rb +2 -2
- data/spec/requests/flows/password_spec.rb +118 -15
- data/spec/requests/flows/refresh_token_spec.rb +89 -19
- data/spec/requests/flows/revoke_token_spec.rb +105 -91
- data/spec/requests/protected_resources/metal_spec.rb +1 -1
- data/spec/requests/protected_resources/private_api_spec.rb +1 -1
- data/spec/routing/custom_controller_routes_spec.rb +4 -0
- data/spec/routing/default_routes_spec.rb +5 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/spec_helper_integration.rb +22 -4
- data/spec/support/dependencies/factory_girl.rb +2 -2
- data/spec/support/helpers/access_token_request_helper.rb +1 -1
- data/spec/support/helpers/model_helper.rb +34 -7
- data/spec/support/helpers/request_spec_helper.rb +17 -5
- data/spec/support/helpers/url_helper.rb +9 -8
- data/spec/support/http_method_shim.rb +38 -0
- data/spec/support/shared/controllers_shared_context.rb +15 -10
- data/spec/support/shared/models_shared_examples.rb +5 -5
- data/spec/validators/redirect_uri_validator_spec.rb +51 -6
- data/spec/version/version_spec.rb +15 -0
- metadata +128 -46
- data/lib/doorkeeper/oauth/client/methods.rb +0 -18
- 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/20130902165751_create_doorkeeper_tables.rb +0 -41
- data/spec/dummy/db/migrate/20141209001746_add_scopes_to_oauth_applications.rb +0 -5
- data/spec/lib/oauth/client/methods_spec.rb +0 -54
@@ -43,19 +43,19 @@ module Doorkeeper::OAuth
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
describe '.
|
46
|
+
describe '.headers' do
|
47
47
|
let(:error_response) { ErrorResponse.new(name: :some_error, state: :some_state) }
|
48
|
-
subject { error_response.
|
48
|
+
subject { error_response.headers }
|
49
49
|
|
50
|
-
it { expect(subject).to include
|
51
|
-
it { expect(subject).to include("error=\"#{error_response.name}\"") }
|
52
|
-
it { expect(subject).to include("error_description=\"#{error_response.description}\"") }
|
53
|
-
end
|
50
|
+
it { expect(subject).to include 'WWW-Authenticate' }
|
54
51
|
|
55
|
-
|
56
|
-
|
52
|
+
describe "WWW-Authenticate header" do
|
53
|
+
subject { error_response.headers["WWW-Authenticate"] }
|
57
54
|
|
58
|
-
|
55
|
+
it { expect(subject).to include("realm=\"#{error_response.realm}\"") }
|
56
|
+
it { expect(subject).to include("error=\"#{error_response.name}\"") }
|
57
|
+
it { expect(subject).to include("error_description=\"#{error_response.description}\"") }
|
58
|
+
end
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -13,7 +13,7 @@ module Doorkeeper::OAuth
|
|
13
13
|
it 'is translated from translation messages' do
|
14
14
|
expect(I18n).to receive(:translate).with(
|
15
15
|
:some_error,
|
16
|
-
scope: [
|
16
|
+
scope: %i[doorkeeper errors messages],
|
17
17
|
default: :server_error
|
18
18
|
)
|
19
19
|
error.description
|
@@ -5,6 +5,11 @@ require 'doorkeeper/oauth/helpers/uri_checker'
|
|
5
5
|
module Doorkeeper::OAuth::Helpers
|
6
6
|
describe URIChecker do
|
7
7
|
describe '.valid?' do
|
8
|
+
it 'is valid for native uris' do
|
9
|
+
uri = 'urn:ietf:wg:oauth:2.0:oob'
|
10
|
+
expect(URIChecker.valid?(uri)).to be_truthy
|
11
|
+
end
|
12
|
+
|
8
13
|
it 'is valid for valid uris' do
|
9
14
|
uri = 'http://app.co'
|
10
15
|
expect(URIChecker.valid?(uri)).to be_truthy
|
@@ -39,6 +44,11 @@ module Doorkeeper::OAuth::Helpers
|
|
39
44
|
uri = 'http://'
|
40
45
|
expect(URIChecker.valid?(uri)).to be_falsey
|
41
46
|
end
|
47
|
+
|
48
|
+
it 'is invalid if is not an uri' do
|
49
|
+
uri = ' '
|
50
|
+
expect(URIChecker.valid?(uri)).to be_falsey
|
51
|
+
end
|
42
52
|
end
|
43
53
|
|
44
54
|
describe '.matches?' do
|
@@ -64,6 +74,44 @@ module Doorkeeper::OAuth::Helpers
|
|
64
74
|
client_uri = 'http://example.com?app.co=test'
|
65
75
|
expect(URIChecker.matches?(uri, client_uri)).to be_falsey
|
66
76
|
end
|
77
|
+
|
78
|
+
context "client registered query params" do
|
79
|
+
it "doesn't allow query being absent" do
|
80
|
+
uri = 'http://app.co'
|
81
|
+
client_uri = 'http://app.co/?vendorId=AJ4L7XXW9'
|
82
|
+
expect(URIChecker.matches?(uri, client_uri)).to be_falsey
|
83
|
+
end
|
84
|
+
|
85
|
+
it "is false if query values differ but key same" do
|
86
|
+
uri = 'http://app.co/?vendorId=pancakes'
|
87
|
+
client_uri = 'http://app.co/?vendorId=waffles'
|
88
|
+
expect(URIChecker.matches?(uri, client_uri)).to be_falsey
|
89
|
+
end
|
90
|
+
|
91
|
+
it "is false if query values same but key differs" do
|
92
|
+
uri = 'http://app.co/?foo=pancakes'
|
93
|
+
client_uri = 'http://app.co/?bar=pancakes'
|
94
|
+
expect(URIChecker.matches?(uri, client_uri)).to be_falsey
|
95
|
+
end
|
96
|
+
|
97
|
+
it "is false if query present and match, but unknown queries present" do
|
98
|
+
uri = 'http://app.co/?vendorId=pancakes&unknown=query'
|
99
|
+
client_uri = 'http://app.co/?vendorId=waffles'
|
100
|
+
expect(URIChecker.matches?(uri, client_uri)).to be_falsey
|
101
|
+
end
|
102
|
+
|
103
|
+
it "is true if queries are present and matche" do
|
104
|
+
uri = 'http://app.co/?vendorId=AJ4L7XXW9&foo=bar'
|
105
|
+
client_uri = 'http://app.co/?vendorId=AJ4L7XXW9&foo=bar'
|
106
|
+
expect(URIChecker.matches?(uri, client_uri)).to be_truthy
|
107
|
+
end
|
108
|
+
|
109
|
+
it "is true if queries are present, match and in different order" do
|
110
|
+
uri = 'http://app.co/?bing=bang&foo=bar'
|
111
|
+
client_uri = 'http://app.co/?foo=bar&bing=bang'
|
112
|
+
expect(URIChecker.matches?(uri, client_uri)).to be_truthy
|
113
|
+
end
|
114
|
+
end
|
67
115
|
end
|
68
116
|
|
69
117
|
describe '.valid_for_authorization?' do
|
@@ -96,9 +144,75 @@ module Doorkeeper::OAuth::Helpers
|
|
96
144
|
end
|
97
145
|
|
98
146
|
it 'is false if invalid' do
|
99
|
-
uri =
|
147
|
+
uri = 'http://app.co/aaa?pankcakes=abc'
|
148
|
+
client_uri = 'http://app.co/aaa?waffles=abc'
|
100
149
|
expect(URIChecker.valid_for_authorization?(uri, client_uri)).to be false
|
101
150
|
end
|
151
|
+
|
152
|
+
it 'calls .matches?' do
|
153
|
+
uri = 'http://app.co/aaa?pankcakes=abc'
|
154
|
+
client_uri = 'http://app.co/aaa?waffles=abc'
|
155
|
+
expect(URIChecker).to receive(:matches?).with(uri, client_uri).once
|
156
|
+
URIChecker.valid_for_authorization?(uri, client_uri)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'calls .valid?' do
|
160
|
+
uri = 'http://app.co/aaa?pankcakes=abc'
|
161
|
+
client_uri = 'http://app.co/aaa?waffles=abc'
|
162
|
+
expect(URIChecker).to receive(:valid?).with(uri).once
|
163
|
+
URIChecker.valid_for_authorization?(uri, client_uri)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '.query_matches?' do
|
168
|
+
it 'is true if no queries' do
|
169
|
+
expect(URIChecker.query_matches?('', '')).to be_truthy
|
170
|
+
expect(URIChecker.query_matches?(nil, nil)).to be_truthy
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'is true if same query' do
|
174
|
+
expect(URIChecker.query_matches?('foo', 'foo')).to be_truthy
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'is false if different query' do
|
178
|
+
expect(URIChecker.query_matches?('foo', 'bar')).to be_falsey
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'is true if same queries' do
|
182
|
+
expect(URIChecker.query_matches?('foo&bar', 'foo&bar')).to be_truthy
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'is true if same queries, different order' do
|
186
|
+
expect(URIChecker.query_matches?('foo&bar', 'bar&foo')).to be_truthy
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'is false if one different query' do
|
190
|
+
expect(URIChecker.query_matches?('foo&bang', 'foo&bing')).to be_falsey
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'is true if same query with same value' do
|
194
|
+
expect(URIChecker.query_matches?('foo=bar', 'foo=bar')).to be_truthy
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'is true if same queries with same values' do
|
198
|
+
expect(URIChecker.query_matches?('foo=bar&bing=bang', 'foo=bar&bing=bang')).to be_truthy
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'is true if same queries with same values, different order' do
|
202
|
+
expect(URIChecker.query_matches?('foo=bar&bing=bang', 'bing=bang&foo=bar')).to be_truthy
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'is false if same query with different value' do
|
206
|
+
expect(URIChecker.query_matches?('foo=bar', 'foo=bang')).to be_falsey
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'is false if some queries missing' do
|
210
|
+
expect(URIChecker.query_matches?('foo=bar', 'foo=bar&bing=bang')).to be_falsey
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'is false if some queries different value' do
|
214
|
+
expect(URIChecker.query_matches?('foo=bar&bing=bang', 'foo=bar&bing=banana')).to be_falsey
|
215
|
+
end
|
102
216
|
end
|
103
217
|
end
|
104
218
|
end
|
@@ -5,23 +5,51 @@ require 'doorkeeper/oauth/invalid_token_response'
|
|
5
5
|
|
6
6
|
module Doorkeeper::OAuth
|
7
7
|
describe InvalidTokenResponse do
|
8
|
-
describe
|
8
|
+
describe "#name" do
|
9
9
|
it { expect(subject.name).to eq(:invalid_token) }
|
10
10
|
end
|
11
11
|
|
12
|
-
describe
|
12
|
+
describe "#status" do
|
13
13
|
it { expect(subject.status).to eq(:unauthorized) }
|
14
14
|
end
|
15
15
|
|
16
16
|
describe :from_access_token do
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
let(:response) { InvalidTokenResponse.from_access_token(access_token) }
|
18
|
+
|
19
|
+
context "revoked" do
|
20
|
+
let(:access_token) { double(revoked?: true, expired?: true) }
|
21
|
+
|
22
|
+
it "sets a description" do
|
23
|
+
expect(response.description).to include("revoked")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "sets the reason" do
|
27
|
+
expect(response.reason).to eq(:revoked)
|
28
|
+
end
|
20
29
|
end
|
21
30
|
|
22
|
-
|
23
|
-
|
24
|
-
|
31
|
+
context "expired" do
|
32
|
+
let(:access_token) { double(revoked?: false, expired?: true) }
|
33
|
+
|
34
|
+
it "sets a description" do
|
35
|
+
expect(response.description).to include("expired")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "sets the reason" do
|
39
|
+
expect(response.reason).to eq(:expired)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "unknown" do
|
44
|
+
let(:access_token) { double(revoked?: false, expired?: false) }
|
45
|
+
|
46
|
+
it "sets a description" do
|
47
|
+
expect(response.description).to include("invalid")
|
48
|
+
end
|
49
|
+
|
50
|
+
it "sets the reason" do
|
51
|
+
expect(response.reason).to eq(:unknown)
|
52
|
+
end
|
25
53
|
end
|
26
54
|
end
|
27
55
|
end
|
@@ -11,23 +11,22 @@ module Doorkeeper::OAuth
|
|
11
11
|
custom_access_token_expires_in: ->(_app) { nil }
|
12
12
|
)
|
13
13
|
end
|
14
|
-
let(:
|
15
|
-
let(:client) { FactoryGirl.create(:application) }
|
14
|
+
let(:client) { FactoryBot.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,12 +48,12 @@ 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
|
|
55
55
|
it 'creates token even when there is already one (default)' do
|
56
|
-
|
56
|
+
FactoryBot.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
|
57
57
|
expect do
|
58
58
|
subject.authorize
|
59
59
|
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
@@ -61,12 +61,18 @@ module Doorkeeper::OAuth
|
|
61
61
|
|
62
62
|
it 'skips token creation if there is already one' do
|
63
63
|
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
64
|
-
|
64
|
+
FactoryBot.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
|
65
65
|
expect do
|
66
66
|
subject.authorize
|
67
67
|
end.to_not change { Doorkeeper::AccessToken.count }
|
68
68
|
end
|
69
69
|
|
70
|
+
it "calls configured request callback methods" do
|
71
|
+
expect(Doorkeeper.configuration.before_successful_strategy_response).to receive(:call).with(subject).once
|
72
|
+
expect(Doorkeeper.configuration.after_successful_strategy_response).to receive(:call).with(subject, instance_of(Doorkeeper::OAuth::TokenResponse)).once
|
73
|
+
subject.authorize
|
74
|
+
end
|
75
|
+
|
70
76
|
describe 'with scopes' do
|
71
77
|
subject do
|
72
78
|
PasswordAccessTokenRequest.new(server, client, owner, scope: 'public')
|
@@ -123,14 +123,19 @@ module Doorkeeper::OAuth
|
|
123
123
|
expect(subject.scopes).to eq(Scopes.from_string('default'))
|
124
124
|
end
|
125
125
|
|
126
|
-
|
127
|
-
|
128
|
-
expect(subject).to be_authorizable
|
129
|
-
end
|
126
|
+
context 'with native redirect uri' do
|
127
|
+
let(:native_redirect_uri) { 'urn:ietf:wg:oauth:2.0:oob' }
|
130
128
|
|
131
|
-
|
132
|
-
|
133
|
-
|
129
|
+
it 'accepts redirect_uri when it matches with the client' do
|
130
|
+
subject.redirect_uri = native_redirect_uri
|
131
|
+
allow(subject.client).to receive(:redirect_uri) { native_redirect_uri }
|
132
|
+
expect(subject).to be_authorizable
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'invalidates redirect_uri when it does\'n match with the client' do
|
136
|
+
subject.redirect_uri = native_redirect_uri
|
137
|
+
expect(subject).not_to be_authorizable
|
138
|
+
end
|
134
139
|
end
|
135
140
|
|
136
141
|
it 'stores the state' do
|
@@ -2,24 +2,29 @@ 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
|
8
|
+
|
5
9
|
let(:server) do
|
6
10
|
double :server,
|
7
11
|
access_token_expires_in: 2.minutes,
|
8
12
|
custom_access_token_expires_in: -> (_oauth_client) { nil }
|
9
13
|
end
|
14
|
+
|
10
15
|
let(:refresh_token) do
|
11
|
-
|
16
|
+
FactoryBot.create(:access_token, use_refresh_token: true)
|
12
17
|
end
|
18
|
+
|
13
19
|
let(:client) { refresh_token.application }
|
14
20
|
let(:credentials) { Client::Credentials.new(client.uid, client.secret) }
|
15
21
|
|
16
22
|
subject { RefreshTokenRequest.new server, refresh_token, credentials }
|
17
23
|
|
18
24
|
it 'issues a new token for the client' do
|
19
|
-
expect
|
20
|
-
|
21
|
-
|
22
|
-
expect(client.reload.access_tokens.last.expires_in).to eq(120)
|
25
|
+
expect { subject.authorize }.to change { client.reload.access_tokens.count }.by(1)
|
26
|
+
# #sort_by used for MongoDB ORM extensions for valid ordering
|
27
|
+
expect(client.reload.access_tokens.sort_by(&:created_at).last.expires_in).to eq(120)
|
23
28
|
end
|
24
29
|
|
25
30
|
it 'issues a new token for the client with custom expires_in' do
|
@@ -27,15 +32,24 @@ module Doorkeeper::OAuth
|
|
27
32
|
access_token_expires_in: 2.minutes,
|
28
33
|
custom_access_token_expires_in: ->(_oauth_client) { 1234 }
|
29
34
|
|
35
|
+
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
|
36
|
+
|
30
37
|
RefreshTokenRequest.new(server, refresh_token, credentials).authorize
|
31
38
|
|
32
|
-
|
39
|
+
# #sort_by used for MongoDB ORM extensions for valid ordering
|
40
|
+
expect(client.reload.access_tokens.sort_by(&:created_at).last.expires_in).to eq(1234)
|
33
41
|
end
|
34
42
|
|
35
43
|
it 'revokes the previous token' do
|
36
44
|
expect { subject.authorize }.to change { refresh_token.revoked? }.from(false).to(true)
|
37
45
|
end
|
38
46
|
|
47
|
+
it "calls configured request callback methods" do
|
48
|
+
expect(Doorkeeper.configuration.before_successful_strategy_response).to receive(:call).with(subject).once
|
49
|
+
expect(Doorkeeper.configuration.after_successful_strategy_response).to receive(:call).with(subject, instance_of(Doorkeeper::OAuth::TokenResponse)).once
|
50
|
+
subject.authorize
|
51
|
+
end
|
52
|
+
|
39
53
|
it 'requires the refresh token' do
|
40
54
|
subject.refresh_token = nil
|
41
55
|
subject.validate
|
@@ -49,7 +63,7 @@ module Doorkeeper::OAuth
|
|
49
63
|
end
|
50
64
|
|
51
65
|
it "requires the token's client and current client to match" do
|
52
|
-
subject.client =
|
66
|
+
subject.client = FactoryBot.create(:application)
|
53
67
|
subject.validate
|
54
68
|
expect(subject.error).to eq(:invalid_grant)
|
55
69
|
end
|
@@ -67,8 +81,37 @@ module Doorkeeper::OAuth
|
|
67
81
|
expect(subject).to be_valid
|
68
82
|
end
|
69
83
|
|
84
|
+
context 'refresh tokens expire on access token use' do
|
85
|
+
let(:server) do
|
86
|
+
double :server,
|
87
|
+
access_token_expires_in: 2.minutes,
|
88
|
+
custom_access_token_expires_in: ->(_oauth_client) { 1234 }
|
89
|
+
end
|
90
|
+
|
91
|
+
before do
|
92
|
+
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(true)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'issues a new token for the client' do
|
96
|
+
expect { subject.authorize }.to change { client.reload.access_tokens.count }.by(1)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'does not revoke the previous token' do
|
100
|
+
subject.authorize
|
101
|
+
expect(refresh_token).not_to be_revoked
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'sets the previous refresh token in the new access token' do
|
105
|
+
subject.authorize
|
106
|
+
expect(
|
107
|
+
# #sort_by used for MongoDB ORM extensions for valid ordering
|
108
|
+
client.access_tokens.sort_by(&:created_at).last.previous_refresh_token
|
109
|
+
).to eq(refresh_token.refresh_token)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
70
113
|
context 'clientless access tokens' do
|
71
|
-
let!(:refresh_token) {
|
114
|
+
let!(:refresh_token) { FactoryBot.create(:clientless_access_token, use_refresh_token: true) }
|
72
115
|
|
73
116
|
subject { RefreshTokenRequest.new server, refresh_token, nil }
|
74
117
|
|
@@ -79,7 +122,7 @@ module Doorkeeper::OAuth
|
|
79
122
|
|
80
123
|
context 'with scopes' do
|
81
124
|
let(:refresh_token) do
|
82
|
-
|
125
|
+
FactoryBot.create :access_token,
|
83
126
|
use_refresh_token: true,
|
84
127
|
scopes: 'public write'
|
85
128
|
end
|
@@ -62,15 +62,19 @@ module Doorkeeper::OAuth
|
|
62
62
|
describe '#+' do
|
63
63
|
it 'can add to another scope object' do
|
64
64
|
scopes = Scopes.from_string('public') + Scopes.from_string('admin')
|
65
|
-
expect(scopes.all).to eq(%w
|
65
|
+
expect(scopes.all).to eq(%w[public admin])
|
66
66
|
end
|
67
67
|
|
68
68
|
it 'does not change the existing object' do
|
69
69
|
origin = Scopes.from_string('public')
|
70
|
-
new_scope = origin + Scopes.from_string('admin')
|
71
70
|
expect(origin.to_s).to eq('public')
|
72
71
|
end
|
73
72
|
|
73
|
+
it 'can add an array to a scope object' do
|
74
|
+
scopes = Scopes.from_string('public') + ['admin']
|
75
|
+
expect(scopes.all).to eq(%w[public admin])
|
76
|
+
end
|
77
|
+
|
74
78
|
it 'raises an error if cannot handle addition' do
|
75
79
|
expect do
|
76
80
|
Scopes.from_string('public') + 'admin'
|
@@ -78,6 +82,24 @@ module Doorkeeper::OAuth
|
|
78
82
|
end
|
79
83
|
end
|
80
84
|
|
85
|
+
describe '#&' do
|
86
|
+
it 'can get intersection with another scope object' do
|
87
|
+
scopes = Scopes.from_string('public admin') & Scopes.from_string('write admin')
|
88
|
+
expect(scopes.all).to eq(%w[admin])
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'does not change the existing object' do
|
92
|
+
origin = Scopes.from_string('public admin')
|
93
|
+
origin & Scopes.from_string('write admin')
|
94
|
+
expect(origin.to_s).to eq('public admin')
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'can get intersection with an array' do
|
98
|
+
scopes = Scopes.from_string('public admin') & %w[write admin]
|
99
|
+
expect(scopes.all).to eq(%w[admin])
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
81
103
|
describe '#==' do
|
82
104
|
it 'is equal to another set of scopes' do
|
83
105
|
expect(Scopes.from_string('public')).to eq(Scopes.from_string('public'))
|
@@ -90,6 +112,10 @@ module Doorkeeper::OAuth
|
|
90
112
|
it 'differs from another set of scopes when scopes are not the same' do
|
91
113
|
expect(Scopes.from_string('public write')).not_to eq(Scopes.from_string('write'))
|
92
114
|
end
|
115
|
+
|
116
|
+
it "does not raise an error when compared to a non-enumerable object" do
|
117
|
+
expect { Scopes.from_string("public") == false }.not_to raise_error
|
118
|
+
end
|
93
119
|
end
|
94
120
|
|
95
121
|
describe '#has_scopes?' do
|
@@ -6,6 +6,7 @@ module Doorkeeper::OAuth
|
|
6
6
|
scopes = double(all: ['public'])
|
7
7
|
double(:application, id: 9990, scopes: scopes)
|
8
8
|
end
|
9
|
+
|
9
10
|
let :pre_auth do
|
10
11
|
double(
|
11
12
|
:pre_auth,
|
@@ -38,9 +39,7 @@ module Doorkeeper::OAuth
|
|
38
39
|
|
39
40
|
it 'does not create token when not authorizable' do
|
40
41
|
allow(pre_auth).to receive(:authorizable?).and_return(false)
|
41
|
-
expect
|
42
|
-
subject.authorize
|
43
|
-
end.to_not change { Doorkeeper::AccessToken.count }
|
42
|
+
expect { subject.authorize }.not_to change { Doorkeeper::AccessToken.count }
|
44
43
|
end
|
45
44
|
|
46
45
|
it 'returns a error response' do
|
@@ -75,7 +74,7 @@ module Doorkeeper::OAuth
|
|
75
74
|
|
76
75
|
it 'creates a new token if scopes do not match' do
|
77
76
|
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
78
|
-
|
77
|
+
FactoryBot.create(:access_token, application_id: pre_auth.client.id,
|
79
78
|
resource_owner_id: owner.id, scopes: '')
|
80
79
|
expect do
|
81
80
|
subject.authorize
|
@@ -86,12 +85,11 @@ module Doorkeeper::OAuth
|
|
86
85
|
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
87
86
|
allow(application.scopes).to receive(:has_scopes?).and_return(true)
|
88
87
|
allow(application.scopes).to receive(:all?).and_return(true)
|
89
|
-
|
88
|
+
|
89
|
+
FactoryBot.create(:access_token, application_id: pre_auth.client.id,
|
90
90
|
resource_owner_id: owner.id, scopes: 'public')
|
91
91
|
|
92
|
-
expect
|
93
|
-
subject.authorize
|
94
|
-
end.to_not change { Doorkeeper::AccessToken.count }
|
92
|
+
expect { subject.authorize }.not_to change { Doorkeeper::AccessToken.count }
|
95
93
|
end
|
96
94
|
end
|
97
95
|
end
|
@@ -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
|
data/spec/lib/server_spec.rb
CHANGED
@@ -1,7 +1,4 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'active_support/all'
|
3
|
-
require 'doorkeeper/errors'
|
4
|
-
require 'doorkeeper/server'
|
5
2
|
|
6
3
|
describe Doorkeeper::Server do
|
7
4
|
let(:fake_class) { double :fake_class }
|
@@ -48,5 +45,15 @@ describe Doorkeeper::Server do
|
|
48
45
|
expect(fake_class).to receive(:new).with(subject)
|
49
46
|
subject.authorization_request :code
|
50
47
|
end
|
48
|
+
|
49
|
+
it 'builds the request with composit strategy name' do
|
50
|
+
allow(Doorkeeper.configuration).
|
51
|
+
to receive(:authorization_response_types).
|
52
|
+
and_return(['id_token token'])
|
53
|
+
|
54
|
+
stub_const 'Doorkeeper::Request::IdTokenToken', fake_class
|
55
|
+
expect(fake_class).to receive(:new).with(subject)
|
56
|
+
subject.authorization_request 'id_token token'
|
57
|
+
end
|
51
58
|
end
|
52
59
|
end
|