doorkeeper 5.1.0 → 5.2.0.rc2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +825 -0
- data/CONTRIBUTING.md +11 -9
- data/Dangerfile +2 -2
- data/Dockerfile +29 -0
- data/Gemfile +2 -1
- data/NEWS.md +1 -814
- data/README.md +2 -2
- data/RELEASING.md +6 -5
- data/app/controllers/doorkeeper/applications_controller.rb +2 -0
- data/app/controllers/doorkeeper/authorizations_controller.rb +6 -1
- data/app/controllers/doorkeeper/tokens_controller.rb +32 -9
- data/app/validators/redirect_uri_validator.rb +19 -9
- data/app/views/doorkeeper/applications/_form.html.erb +0 -6
- data/app/views/doorkeeper/applications/show.html.erb +1 -1
- data/config/locales/en.yml +3 -1
- data/doorkeeper.gemspec +1 -1
- data/gemfiles/rails_5_0.gemfile +1 -0
- data/gemfiles/rails_5_1.gemfile +1 -0
- data/gemfiles/rails_5_2.gemfile +1 -0
- data/gemfiles/rails_6_0.gemfile +2 -1
- data/gemfiles/rails_master.gemfile +1 -0
- data/lib/doorkeeper/config/option.rb +13 -7
- data/lib/doorkeeper/config.rb +33 -3
- data/lib/doorkeeper/grape/helpers.rb +5 -1
- data/lib/doorkeeper/helpers/controller.rb +16 -3
- data/lib/doorkeeper/models/access_token_mixin.rb +43 -2
- data/lib/doorkeeper/oauth/authorization/code.rb +10 -8
- data/lib/doorkeeper/oauth/authorization/token.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +14 -0
- data/lib/doorkeeper/oauth/code_response.rb +2 -2
- data/lib/doorkeeper/oauth/error_response.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +18 -4
- data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
- data/lib/doorkeeper/oauth/refresh_token_request.rb +8 -8
- data/lib/doorkeeper/oauth/token_introspection.rb +19 -12
- data/lib/doorkeeper/orm/active_record/access_grant.rb +1 -1
- data/lib/doorkeeper/orm/active_record/access_token.rb +2 -2
- data/lib/doorkeeper/orm/active_record/application.rb +7 -1
- data/lib/doorkeeper/orm/active_record.rb +17 -1
- data/lib/doorkeeper/stale_records_cleaner.rb +6 -2
- data/lib/doorkeeper/version.rb +2 -2
- data/lib/doorkeeper.rb +3 -0
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +6 -6
- data/lib/generators/doorkeeper/templates/initializer.rb +57 -12
- data/lib/generators/doorkeeper/templates/migration.rb.erb +3 -0
- data/spec/controllers/applications_controller_spec.rb +93 -0
- data/spec/controllers/authorizations_controller_spec.rb +13 -0
- data/spec/controllers/tokens_controller_spec.rb +205 -37
- data/spec/dummy/config/application.rb +3 -1
- data/spec/dummy/config/initializers/doorkeeper.rb +54 -9
- data/spec/lib/config_spec.rb +11 -0
- data/spec/lib/oauth/client_credentials/creator_spec.rb +3 -0
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +17 -2
- data/spec/lib/oauth/pre_authorization_spec.rb +0 -15
- data/spec/requests/flows/authorization_code_spec.rb +16 -4
- data/spec/requests/flows/revoke_token_spec.rb +19 -11
- data/spec/support/doorkeeper_rspec.rb +1 -1
- data/spec/validators/redirect_uri_validator_spec.rb +39 -14
- metadata +7 -13
- data/.coveralls.yml +0 -1
- data/.github/ISSUE_TEMPLATE.md +0 -25
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -17
- data/.gitignore +0 -20
- data/.gitlab-ci.yml +0 -16
- data/.hound.yml +0 -3
- data/.rspec +0 -1
- data/.rubocop.yml +0 -50
- data/.travis.yml +0 -35
|
@@ -3,31 +3,139 @@
|
|
|
3
3
|
require "spec_helper"
|
|
4
4
|
|
|
5
5
|
describe Doorkeeper::TokensController do
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
let(:client) { FactoryBot.create :application }
|
|
7
|
+
let!(:user) { User.create!(name: "Joe", password: "sekret") }
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
before do
|
|
10
|
+
Doorkeeper.configure do
|
|
11
|
+
resource_owner_from_credentials do
|
|
12
|
+
User.first
|
|
13
|
+
end
|
|
14
|
+
end
|
|
11
15
|
|
|
12
|
-
|
|
16
|
+
allow(Doorkeeper.configuration).to receive(:grant_flows).and_return(["password"])
|
|
17
|
+
end
|
|
13
18
|
|
|
14
|
-
|
|
19
|
+
subject { JSON.parse(response.body) }
|
|
20
|
+
|
|
21
|
+
describe "POST #create" do
|
|
22
|
+
before do
|
|
23
|
+
post :create, params: {
|
|
24
|
+
client_id: client.uid,
|
|
25
|
+
client_secret: client.secret,
|
|
26
|
+
grant_type: "password",
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "responds after authorization" do
|
|
31
|
+
expect(response).to be_successful
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "includes access token in response" do
|
|
35
|
+
expect(subject["access_token"]).to eq(Doorkeeper::AccessToken.first.token)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "includes token type in response" do
|
|
39
|
+
expect(subject["token_type"]).to eq("Bearer")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "includes token expiration in response" do
|
|
43
|
+
expect(subject["expires_in"].to_i).to eq(Doorkeeper.configuration.access_token_expires_in)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "issues the token for the current client" do
|
|
47
|
+
expect(Doorkeeper::AccessToken.first.application_id).to eq(client.id)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "issues the token for the current resource owner" do
|
|
51
|
+
expect(Doorkeeper::AccessToken.first.resource_owner_id).to eq(user.id)
|
|
15
52
|
end
|
|
16
53
|
end
|
|
17
54
|
|
|
18
|
-
describe "
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
55
|
+
describe "POST #create with errors" do
|
|
56
|
+
before do
|
|
57
|
+
post :create, params: {
|
|
58
|
+
client_id: client.uid,
|
|
59
|
+
client_secret: "invalid",
|
|
60
|
+
grant_type: "password",
|
|
61
|
+
}
|
|
62
|
+
end
|
|
22
63
|
|
|
23
|
-
|
|
64
|
+
it "responds after authorization" do
|
|
65
|
+
expect(response).to be_unauthorized
|
|
66
|
+
end
|
|
24
67
|
|
|
25
|
-
|
|
26
|
-
expect(
|
|
68
|
+
it "include error in response" do
|
|
69
|
+
expect(subject["error"]).to eq("invalid_client")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "include error_description in response" do
|
|
73
|
+
expect(subject["error_description"]).to be
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "does not include access token in response" do
|
|
77
|
+
expect(subject["access_token"]).to be_nil
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "does not include token type in response" do
|
|
81
|
+
expect(subject["token_type"]).to be_nil
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "does not include token expiration in response" do
|
|
85
|
+
expect(subject["expires_in"]).to be_nil
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "does not issue any access token" do
|
|
89
|
+
expect(Doorkeeper::AccessToken.all).to be_empty
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe "POST #create with callbacks" do
|
|
94
|
+
after do
|
|
95
|
+
client.update_attribute :redirect_uri, "urn:ietf:wg:oauth:2.0:oob"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
describe "when successful" do
|
|
99
|
+
after do
|
|
100
|
+
post :create, params: {
|
|
101
|
+
client_id: client.uid,
|
|
102
|
+
client_secret: client.secret,
|
|
103
|
+
grant_type: "password",
|
|
104
|
+
}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "should call :before_successful_authorization callback" do
|
|
108
|
+
expect(Doorkeeper.configuration)
|
|
109
|
+
.to receive_message_chain(:before_successful_authorization, :call).with(instance_of(described_class))
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it "should call :after_successful_authorization callback" do
|
|
113
|
+
expect(Doorkeeper.configuration)
|
|
114
|
+
.to receive_message_chain(:after_successful_authorization, :call).with(instance_of(described_class))
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
describe "with errors" do
|
|
119
|
+
after do
|
|
120
|
+
post :create, params: {
|
|
121
|
+
client_id: client.uid,
|
|
122
|
+
client_secret: "invalid",
|
|
123
|
+
grant_type: "password",
|
|
124
|
+
}
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "should call :before_successful_authorization callback" do
|
|
128
|
+
expect(Doorkeeper.configuration)
|
|
129
|
+
.to receive_message_chain(:before_successful_authorization, :call).with(instance_of(described_class))
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it "should not call :after_successful_authorization callback" do
|
|
133
|
+
expect(Doorkeeper.configuration).not_to receive(:after_successful_authorization)
|
|
134
|
+
end
|
|
27
135
|
end
|
|
28
136
|
end
|
|
29
137
|
|
|
30
|
-
describe "
|
|
138
|
+
describe "POST #create with custom error" do
|
|
31
139
|
it "returns the error response with a custom message" do
|
|
32
140
|
# I18n looks for `doorkeeper.errors.messages.custom_message` in locale files
|
|
33
141
|
custom_message = "my_message"
|
|
@@ -58,7 +166,7 @@ describe Doorkeeper::TokensController do
|
|
|
58
166
|
end
|
|
59
167
|
|
|
60
168
|
# http://tools.ietf.org/html/rfc7009#section-2.2
|
|
61
|
-
describe "
|
|
169
|
+
describe "POST #revoke" do
|
|
62
170
|
let(:client) { FactoryBot.create(:application) }
|
|
63
171
|
let(:access_token) { FactoryBot.create(:access_token, application: client) }
|
|
64
172
|
|
|
@@ -102,10 +210,10 @@ describe Doorkeeper::TokensController do
|
|
|
102
210
|
let(:some_other_client) { FactoryBot.create(:application, confidential: true) }
|
|
103
211
|
let(:oauth_client) { Doorkeeper::OAuth::Client.new(some_other_client) }
|
|
104
212
|
|
|
105
|
-
it "returns
|
|
213
|
+
it "returns 403" do
|
|
106
214
|
post :revoke, params: { token: access_token.token }
|
|
107
215
|
|
|
108
|
-
expect(response.status).to eq
|
|
216
|
+
expect(response.status).to eq 403
|
|
109
217
|
end
|
|
110
218
|
|
|
111
219
|
it "does not revoke the access token" do
|
|
@@ -117,21 +225,7 @@ describe Doorkeeper::TokensController do
|
|
|
117
225
|
end
|
|
118
226
|
end
|
|
119
227
|
|
|
120
|
-
describe "
|
|
121
|
-
it "memoizes the result of the authorization" do
|
|
122
|
-
strategy = double(:strategy, authorize: true)
|
|
123
|
-
expect(strategy).to receive(:authorize).once
|
|
124
|
-
allow(controller).to receive(:strategy) { strategy }
|
|
125
|
-
allow(controller).to receive(:create) do
|
|
126
|
-
2.times { controller.send :authorize_response }
|
|
127
|
-
controller.render json: {}, status: :ok
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
post :create
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
describe "when requested token introspection" do
|
|
228
|
+
describe "POST #introspect" do
|
|
135
229
|
let(:client) { FactoryBot.create(:application) }
|
|
136
230
|
let(:access_token) { FactoryBot.create(:access_token, application: client) }
|
|
137
231
|
let(:token_for_introspection) { FactoryBot.create(:access_token, application: client) }
|
|
@@ -147,7 +241,7 @@ describe Doorkeeper::TokensController do
|
|
|
147
241
|
end
|
|
148
242
|
end
|
|
149
243
|
|
|
150
|
-
context "authorized using
|
|
244
|
+
context "authorized using Client Credentials of the client that token is issued to" do
|
|
151
245
|
it "responds with full token introspection" do
|
|
152
246
|
request.headers["Authorization"] = basic_auth_header_for_client(client)
|
|
153
247
|
|
|
@@ -159,6 +253,26 @@ describe Doorkeeper::TokensController do
|
|
|
159
253
|
end
|
|
160
254
|
end
|
|
161
255
|
|
|
256
|
+
context "configured token introspection disabled" do
|
|
257
|
+
before do
|
|
258
|
+
Doorkeeper.configure do
|
|
259
|
+
orm DOORKEEPER_ORM
|
|
260
|
+
allow_token_introspection false
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
it "responds with invalid_token error" do
|
|
265
|
+
request.headers["Authorization"] = "Bearer #{access_token.token}"
|
|
266
|
+
|
|
267
|
+
post :introspect, params: { token: token_for_introspection.token }
|
|
268
|
+
|
|
269
|
+
response_status_should_be 401
|
|
270
|
+
|
|
271
|
+
should_not_have_json "active"
|
|
272
|
+
should_have_json "error", "invalid_token"
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
162
276
|
context "using custom introspection response" do
|
|
163
277
|
before do
|
|
164
278
|
Doorkeeper.configure do
|
|
@@ -212,12 +326,64 @@ describe Doorkeeper::TokensController do
|
|
|
212
326
|
end
|
|
213
327
|
end
|
|
214
328
|
|
|
329
|
+
context "introspection request authorized by a client and allow_token_introspection is true" do
|
|
330
|
+
let(:different_client) { FactoryBot.create(:application) }
|
|
331
|
+
|
|
332
|
+
before do
|
|
333
|
+
allow(Doorkeeper.configuration).to receive(:allow_token_introspection).and_return(proc do
|
|
334
|
+
true
|
|
335
|
+
end)
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
it "responds with full token introspection" do
|
|
339
|
+
request.headers["Authorization"] = basic_auth_header_for_client(different_client)
|
|
340
|
+
|
|
341
|
+
post :introspect, params: { token: token_for_introspection.token }
|
|
342
|
+
|
|
343
|
+
should_have_json "active", true
|
|
344
|
+
expect(json_response).to include("client_id", "token_type", "exp", "iat")
|
|
345
|
+
should_have_json "client_id", client.uid
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
context "allow_token_introspection requires authorized token with special scope" do
|
|
350
|
+
let(:access_token) { FactoryBot.create(:access_token, scopes: "introspection") }
|
|
351
|
+
|
|
352
|
+
before do
|
|
353
|
+
allow(Doorkeeper.configuration).to receive(:allow_token_introspection).and_return(proc do |_token, _client, authorized_token|
|
|
354
|
+
authorized_token.scopes.include?("introspection")
|
|
355
|
+
end)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
it "responds with full token introspection if authorized token has introspection scope" do
|
|
359
|
+
request.headers["Authorization"] = "Bearer #{access_token.token}"
|
|
360
|
+
|
|
361
|
+
post :introspect, params: { token: token_for_introspection.token }
|
|
362
|
+
|
|
363
|
+
should_have_json "active", true
|
|
364
|
+
expect(json_response).to include("client_id", "token_type", "exp", "iat")
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
it "responds with invalid_token error if authorized token doesn't have introspection scope" do
|
|
368
|
+
access_token.update(scopes: "read write")
|
|
369
|
+
|
|
370
|
+
request.headers["Authorization"] = "Bearer #{access_token.token}"
|
|
371
|
+
|
|
372
|
+
post :introspect, params: { token: token_for_introspection.token }
|
|
373
|
+
|
|
374
|
+
response_status_should_be 401
|
|
375
|
+
|
|
376
|
+
should_not_have_json "active"
|
|
377
|
+
should_have_json "error", "invalid_token"
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
215
381
|
context "authorized using invalid Bearer token" do
|
|
216
382
|
let(:access_token) do
|
|
217
383
|
FactoryBot.create(:access_token, application: client, revoked_at: 1.day.ago)
|
|
218
384
|
end
|
|
219
385
|
|
|
220
|
-
it "responds with
|
|
386
|
+
it "responds with invalid_token error" do
|
|
221
387
|
request.headers["Authorization"] = "Bearer #{access_token.token}"
|
|
222
388
|
|
|
223
389
|
post :introspect, params: { token: token_for_introspection.token }
|
|
@@ -272,13 +438,15 @@ describe Doorkeeper::TokensController do
|
|
|
272
438
|
end
|
|
273
439
|
|
|
274
440
|
context "authorized using valid Bearer token" do
|
|
275
|
-
it "responds with
|
|
441
|
+
it "responds with invalid_token error" do
|
|
276
442
|
request.headers["Authorization"] = "Bearer #{access_token.token}"
|
|
277
443
|
|
|
278
444
|
post :introspect, params: { token: SecureRandom.hex(16) }
|
|
279
445
|
|
|
280
|
-
|
|
281
|
-
|
|
446
|
+
response_status_should_be 401
|
|
447
|
+
|
|
448
|
+
should_not_have_json "active"
|
|
449
|
+
should_have_json "error", "invalid_token"
|
|
282
450
|
end
|
|
283
451
|
end
|
|
284
452
|
end
|
|
@@ -5,11 +5,13 @@ require "rails"
|
|
|
5
5
|
%w[
|
|
6
6
|
action_controller/railtie
|
|
7
7
|
action_view/railtie
|
|
8
|
+
action_cable/engine
|
|
8
9
|
sprockets/railtie
|
|
9
10
|
].each do |railtie|
|
|
10
11
|
begin
|
|
11
12
|
require railtie
|
|
12
|
-
rescue LoadError
|
|
13
|
+
rescue LoadError => e
|
|
14
|
+
puts "Error loading '#{railtie}' (#{e.message})"
|
|
13
15
|
end
|
|
14
16
|
end
|
|
15
17
|
|
|
@@ -65,15 +65,6 @@ Doorkeeper.configure do
|
|
|
65
65
|
# Check out the wiki for more information on customization
|
|
66
66
|
# access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param
|
|
67
67
|
|
|
68
|
-
# Change the native redirect uri for client apps
|
|
69
|
-
# When clients register with the following redirect uri, they won't be redirected to any server and
|
|
70
|
-
# the authorization code will be displayed within the provider
|
|
71
|
-
# The value can be any string. Use nil to disable this feature.
|
|
72
|
-
# When disabled, clients must provide a valid URL
|
|
73
|
-
# (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi)
|
|
74
|
-
#
|
|
75
|
-
# native_redirect_uri 'urn:ietf:wg:oauth:2.0:oob'
|
|
76
|
-
|
|
77
68
|
# Forces the usage of the HTTPS protocol in non-native redirect uris (enabled
|
|
78
69
|
# by default in non-development environments). OAuth2 delegates security in
|
|
79
70
|
# communication to the HTTPS protocol so it is wise to keep this enabled.
|
|
@@ -116,6 +107,60 @@ Doorkeeper.configure do
|
|
|
116
107
|
# client.superapp? or resource_owner.admin?
|
|
117
108
|
# end
|
|
118
109
|
|
|
110
|
+
# Configure custom constraints for the Token Introspection request.
|
|
111
|
+
# By default this configuration option allows to introspect a token by another
|
|
112
|
+
# token of the same application, OR to introspect the token that belongs to
|
|
113
|
+
# authorized client (from authenticated client) OR when token doesn't
|
|
114
|
+
# belong to any client (public token). Otherwise requester has no access to the
|
|
115
|
+
# introspection and it will return response as stated in the RFC.
|
|
116
|
+
#
|
|
117
|
+
# Block arguments:
|
|
118
|
+
#
|
|
119
|
+
# @param token [Doorkeeper::AccessToken]
|
|
120
|
+
# token to be introspected
|
|
121
|
+
#
|
|
122
|
+
# @param authorized_client [Doorkeeper::Application]
|
|
123
|
+
# authorized client (if request is authorized using Basic auth with
|
|
124
|
+
# Client Credentials for example)
|
|
125
|
+
#
|
|
126
|
+
# @param authorized_token [Doorkeeper::AccessToken]
|
|
127
|
+
# Bearer token used to authorize the request
|
|
128
|
+
#
|
|
129
|
+
# In case the block returns `nil` or `false` introspection responses with 401 status code
|
|
130
|
+
# when using authorized token to introspect, or you'll get 200 with { "active": false } body
|
|
131
|
+
# when using authorized client to introspect as stated in the
|
|
132
|
+
# RFC 7662 section 2.2. Introspection Response.
|
|
133
|
+
#
|
|
134
|
+
# Using with caution:
|
|
135
|
+
# Keep in mind that these three parameters pass to block can be nil as following case:
|
|
136
|
+
# `authorized_client` is nil if and only if `authorized_token` is present, and vice versa.
|
|
137
|
+
# `token` will be nil if and only if `authorized_token` is present.
|
|
138
|
+
# So remember to use `&` or check if it is present before calling method on
|
|
139
|
+
# them to make sure you doesn't get NoMethodError exception.
|
|
140
|
+
#
|
|
141
|
+
# You can define your custom check:
|
|
142
|
+
#
|
|
143
|
+
# allow_token_introspection do |token, authorized_client, authorized_token|
|
|
144
|
+
# if authorized_token
|
|
145
|
+
# # customize: require `introspection` scope
|
|
146
|
+
# authorized_token.application == token&.application ||
|
|
147
|
+
# authorized_token.scopes.include?("introspection")
|
|
148
|
+
# elsif token.application
|
|
149
|
+
# # `protected_resource` is a new database boolean column, for example
|
|
150
|
+
# authorized_client == token.application || authorized_client.protected_resource?
|
|
151
|
+
# else
|
|
152
|
+
# # public token (when token.application is nil, token doesn't belong to any application)
|
|
153
|
+
# true
|
|
154
|
+
# end
|
|
155
|
+
# end
|
|
156
|
+
#
|
|
157
|
+
# Or you can completely disable any token introspection:
|
|
158
|
+
#
|
|
159
|
+
# allow_token_introspection false
|
|
160
|
+
#
|
|
161
|
+
# If you need to block the request at all, then configure your routes.rb or web-server
|
|
162
|
+
# like nginx to forbid the request.
|
|
163
|
+
|
|
119
164
|
# WWW-Authenticate Realm (default "Doorkeeper").
|
|
120
165
|
realm "Doorkeeper"
|
|
121
166
|
end
|
data/spec/lib/config_spec.rb
CHANGED
|
@@ -694,4 +694,15 @@ describe Doorkeeper, "configuration" do
|
|
|
694
694
|
end
|
|
695
695
|
end
|
|
696
696
|
end
|
|
697
|
+
|
|
698
|
+
describe "options deprecation" do
|
|
699
|
+
it "prints a warning message when an option is deprecated" do
|
|
700
|
+
expect(Kernel).to receive(:warn).with(
|
|
701
|
+
"[DOORKEEPER] native_redirect_uri has been deprecated and will soon be removed"
|
|
702
|
+
)
|
|
703
|
+
Doorkeeper.configure do
|
|
704
|
+
native_redirect_uri "urn:ietf:wg:oauth:2.0:oob"
|
|
705
|
+
end
|
|
706
|
+
end
|
|
707
|
+
end
|
|
697
708
|
end
|
|
@@ -55,6 +55,7 @@ class Doorkeeper::OAuth::ClientCredentialsRequest
|
|
|
55
55
|
|
|
56
56
|
expect(Doorkeeper::AccessToken.count).to eq(2)
|
|
57
57
|
expect(result).not_to eq(existing_token)
|
|
58
|
+
expect(existing_token.reload).to be_revoked
|
|
58
59
|
end
|
|
59
60
|
end
|
|
60
61
|
|
|
@@ -69,6 +70,7 @@ class Doorkeeper::OAuth::ClientCredentialsRequest
|
|
|
69
70
|
|
|
70
71
|
expect(Doorkeeper::AccessToken.count).to eq(2)
|
|
71
72
|
expect(result).not_to eq(existing_token)
|
|
73
|
+
expect(existing_token.reload).to be_revoked
|
|
72
74
|
end
|
|
73
75
|
end
|
|
74
76
|
end
|
|
@@ -82,6 +84,7 @@ class Doorkeeper::OAuth::ClientCredentialsRequest
|
|
|
82
84
|
|
|
83
85
|
expect(Doorkeeper::AccessToken.count).to eq(2)
|
|
84
86
|
expect(result).not_to eq(existing_token)
|
|
87
|
+
expect(existing_token.reload).to be_revoked
|
|
85
88
|
end
|
|
86
89
|
end
|
|
87
90
|
|
|
@@ -40,13 +40,28 @@ module Doorkeeper::OAuth::Helpers
|
|
|
40
40
|
expect(URIChecker.valid?(uri)).to be_falsey
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
it "is invalid if localhost is resolved as as scheme (no scheme specified)" do
|
|
44
|
+
uri = "localhost:8080"
|
|
45
|
+
expect(URIChecker.valid?(uri)).to be_falsey
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "is invalid if scheme is missing #2" do
|
|
49
|
+
uri = "app.co:80"
|
|
50
|
+
expect(URIChecker.valid?(uri)).to be_falsey
|
|
51
|
+
end
|
|
52
|
+
|
|
43
53
|
it "is invalid if is not an uri" do
|
|
44
54
|
uri = " "
|
|
45
55
|
expect(URIChecker.valid?(uri)).to be_falsey
|
|
46
56
|
end
|
|
47
57
|
|
|
48
|
-
it "is valid for
|
|
49
|
-
uri = "
|
|
58
|
+
it "is valid for custom schemes" do
|
|
59
|
+
uri = "com.example.app:/test"
|
|
60
|
+
expect(URIChecker.valid?(uri)).to be_truthy
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "is valid for custom schemes with authority marker (common misconfiguration)" do
|
|
64
|
+
uri = "com.example.app://test"
|
|
50
65
|
expect(URIChecker.valid?(uri)).to be_truthy
|
|
51
66
|
end
|
|
52
67
|
end
|
|
@@ -149,21 +149,6 @@ module Doorkeeper::OAuth
|
|
|
149
149
|
expect(subject.scopes).to eq(Scopes.from_string("default"))
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
-
context "with native redirect uri" do
|
|
153
|
-
let(:native_redirect_uri) { "urn:ietf:wg:oauth:2.0:oob" }
|
|
154
|
-
|
|
155
|
-
it "accepts redirect_uri when it matches with the client" do
|
|
156
|
-
subject.redirect_uri = native_redirect_uri
|
|
157
|
-
allow(subject.client).to receive(:redirect_uri) { native_redirect_uri }
|
|
158
|
-
expect(subject).to be_authorizable
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
it "invalidates redirect_uri when it does'n match with the client" do
|
|
162
|
-
subject.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
|
|
163
|
-
expect(subject).not_to be_authorizable
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
|
|
167
152
|
it "matches the redirect uri against client's one" do
|
|
168
153
|
subject.redirect_uri = "http://nothesame.com"
|
|
169
154
|
expect(subject).not_to be_authorizable
|
|
@@ -28,8 +28,8 @@ feature "Authorization Code Flow" do
|
|
|
28
28
|
config_is_set(:token_secret_strategy, ::Doorkeeper::SecretStoring::Sha256Hash)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
@client.redirect_uri =
|
|
31
|
+
def authorize(redirect_url)
|
|
32
|
+
@client.redirect_uri = redirect_url
|
|
33
33
|
@client.save!
|
|
34
34
|
visit authorization_endpoint_url(client: @client)
|
|
35
35
|
click_on "Authorize"
|
|
@@ -42,16 +42,28 @@ feature "Authorization Code Flow" do
|
|
|
42
42
|
hashed_code = Doorkeeper::AccessGrant.secret_strategy.transform_secret code
|
|
43
43
|
expect(hashed_code).to eq Doorkeeper::AccessGrant.first.token
|
|
44
44
|
|
|
45
|
+
[code, hashed_code]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
scenario "using redirect_url urn:ietf:wg:oauth:2.0:oob" do
|
|
49
|
+
code, hashed_code = authorize("urn:ietf:wg:oauth:2.0:oob")
|
|
45
50
|
expect(code).not_to eq(hashed_code)
|
|
51
|
+
i_should_see "Authorization code:"
|
|
52
|
+
i_should_see code
|
|
53
|
+
i_should_not_see hashed_code
|
|
54
|
+
end
|
|
46
55
|
|
|
56
|
+
scenario "using redirect_url urn:ietf:wg:oauth:2.0:oob:auto" do
|
|
57
|
+
code, hashed_code = authorize("urn:ietf:wg:oauth:2.0:oob:auto")
|
|
58
|
+
expect(code).not_to eq(hashed_code)
|
|
47
59
|
i_should_see "Authorization code:"
|
|
48
60
|
i_should_see code
|
|
49
61
|
i_should_not_see hashed_code
|
|
50
62
|
end
|
|
51
63
|
end
|
|
52
64
|
|
|
53
|
-
scenario "resource owner authorizes using
|
|
54
|
-
@client.redirect_uri =
|
|
65
|
+
scenario "resource owner authorizes using oob url" do
|
|
66
|
+
@client.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
|
|
55
67
|
@client.save!
|
|
56
68
|
visit authorization_endpoint_url(client: @client)
|
|
57
69
|
click_on "Authorize"
|
|
@@ -40,16 +40,14 @@ describe "Revoke Token Flow" do
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
context "with invalid token to revoke" do
|
|
43
|
-
it "should not revoke any tokens and respond
|
|
43
|
+
it "should not revoke any tokens and respond with forbidden" do
|
|
44
44
|
expect do
|
|
45
45
|
post revocation_token_endpoint_url,
|
|
46
46
|
params: { token: "I_AM_AN_INVALID_TOKEN" },
|
|
47
47
|
headers: headers
|
|
48
48
|
end.not_to(change { Doorkeeper::AccessToken.where(revoked_at: nil).count })
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
# token is invalid
|
|
52
|
-
expect(response).to be_successful
|
|
50
|
+
expect(response).to be_forbidden
|
|
53
51
|
end
|
|
54
52
|
end
|
|
55
53
|
|
|
@@ -59,19 +57,23 @@ describe "Revoke Token Flow" do
|
|
|
59
57
|
credentials = Base64.encode64("#{client_id}:poop")
|
|
60
58
|
{ "HTTP_AUTHORIZATION" => "Basic #{credentials}" }
|
|
61
59
|
end
|
|
62
|
-
it "should not revoke any tokens and respond
|
|
60
|
+
it "should not revoke any tokens and respond with forbidden" do
|
|
63
61
|
post revocation_token_endpoint_url, params: { token: access_token.token }, headers: headers
|
|
64
62
|
|
|
65
|
-
expect(response).to
|
|
63
|
+
expect(response).to be_forbidden
|
|
64
|
+
expect(response.body).to include("unauthorized_client")
|
|
65
|
+
expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
|
|
66
66
|
expect(access_token.reload.revoked?).to be_falsey
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
context "with no credentials and a valid token" do
|
|
71
|
-
it "should not revoke any tokens and respond
|
|
71
|
+
it "should not revoke any tokens and respond with forbidden" do
|
|
72
72
|
post revocation_token_endpoint_url, params: { token: access_token.token }
|
|
73
73
|
|
|
74
|
-
expect(response).to
|
|
74
|
+
expect(response).to be_forbidden
|
|
75
|
+
expect(response.body).to include("unauthorized_client")
|
|
76
|
+
expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
|
|
75
77
|
expect(access_token.reload.revoked?).to be_falsey
|
|
76
78
|
end
|
|
77
79
|
end
|
|
@@ -88,7 +90,9 @@ describe "Revoke Token Flow" do
|
|
|
88
90
|
it "should not revoke the token as its unauthorized" do
|
|
89
91
|
post revocation_token_endpoint_url, params: { token: access_token.token }, headers: headers
|
|
90
92
|
|
|
91
|
-
expect(response).to
|
|
93
|
+
expect(response).to be_forbidden
|
|
94
|
+
expect(response.body).to include("unauthorized_client")
|
|
95
|
+
expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
|
|
92
96
|
expect(access_token.reload.revoked?).to be_falsey
|
|
93
97
|
end
|
|
94
98
|
end
|
|
@@ -127,14 +131,18 @@ describe "Revoke Token Flow" do
|
|
|
127
131
|
it "should not revoke the access token provided" do
|
|
128
132
|
post revocation_token_endpoint_url, params: { token: access_token.token }
|
|
129
133
|
|
|
130
|
-
expect(response).to
|
|
134
|
+
expect(response).to be_forbidden
|
|
135
|
+
expect(response.body).to include("unauthorized_client")
|
|
136
|
+
expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
|
|
131
137
|
expect(access_token.reload.revoked?).to be_falsey
|
|
132
138
|
end
|
|
133
139
|
|
|
134
140
|
it "should not revoke the refresh token provided" do
|
|
135
141
|
post revocation_token_endpoint_url, params: { token: access_token.token }
|
|
136
142
|
|
|
137
|
-
expect(response).to
|
|
143
|
+
expect(response).to be_forbidden
|
|
144
|
+
expect(response.body).to include("unauthorized_client")
|
|
145
|
+
expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
|
|
138
146
|
expect(access_token.reload.revoked?).to be_falsey
|
|
139
147
|
end
|
|
140
148
|
end
|
|
@@ -16,7 +16,7 @@ module Doorkeeper
|
|
|
16
16
|
# Tries to find ORM from the Gemfile used to run test suite
|
|
17
17
|
def self.detect_orm
|
|
18
18
|
orm = (ENV["BUNDLE_GEMFILE"] || "").match(/Gemfile\.(.+)\.rb/)
|
|
19
|
-
(orm && orm[1] || :active_record).to_sym
|
|
19
|
+
(orm && orm[1] || ENV["ORM"] || :active_record).to_sym
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
end
|