doorkeeper 5.1.0 → 5.3.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.
- checksums.yaml +4 -4
- data/Appraisals +3 -3
- data/CHANGELOG.md +881 -0
- data/CONTRIBUTING.md +11 -9
- data/Dangerfile +2 -2
- data/Dockerfile +29 -0
- data/Gemfile +4 -3
- data/NEWS.md +1 -814
- data/README.md +11 -3
- data/RELEASING.md +6 -5
- data/app/controllers/doorkeeper/application_controller.rb +2 -2
- data/app/controllers/doorkeeper/application_metal_controller.rb +3 -2
- data/app/controllers/doorkeeper/applications_controller.rb +1 -0
- data/app/controllers/doorkeeper/authorizations_controller.rb +16 -9
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +2 -2
- data/app/controllers/doorkeeper/tokens_controller.rb +32 -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 +8 -2
- data/doorkeeper.gemspec +9 -1
- data/gemfiles/rails_5_0.gemfile +3 -2
- data/gemfiles/rails_5_1.gemfile +3 -2
- data/gemfiles/rails_5_2.gemfile +3 -2
- data/gemfiles/rails_6_0.gemfile +4 -3
- data/gemfiles/rails_master.gemfile +3 -2
- data/lib/doorkeeper/config/option.rb +13 -7
- data/lib/doorkeeper/config.rb +157 -42
- data/lib/doorkeeper/errors.rb +13 -18
- data/lib/doorkeeper/grape/helpers.rb +6 -2
- data/lib/doorkeeper/helpers/controller.rb +28 -7
- data/lib/doorkeeper/models/access_grant_mixin.rb +7 -6
- data/lib/doorkeeper/models/access_token_mixin.rb +99 -20
- data/lib/doorkeeper/models/application_mixin.rb +3 -3
- data/lib/doorkeeper/models/concerns/ownership.rb +1 -1
- data/lib/doorkeeper/models/concerns/reusable.rb +1 -1
- data/lib/doorkeeper/models/concerns/revocable.rb +0 -27
- data/lib/doorkeeper/oauth/authorization/code.rb +12 -14
- data/lib/doorkeeper/oauth/authorization/token.rb +10 -7
- data/lib/doorkeeper/oauth/authorization_code_request.rb +31 -15
- data/lib/doorkeeper/oauth/base_request.rb +10 -4
- data/lib/doorkeeper/oauth/client.rb +7 -8
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +23 -2
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +7 -7
- data/lib/doorkeeper/oauth/client_credentials/{validation.rb → validator.rb} +10 -2
- data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -1
- data/lib/doorkeeper/oauth/code_request.rb +5 -11
- data/lib/doorkeeper/oauth/code_response.rb +4 -4
- data/lib/doorkeeper/oauth/error.rb +1 -1
- data/lib/doorkeeper/oauth/error_response.rb +6 -6
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +7 -5
- data/lib/doorkeeper/oauth/helpers/unique_token.rb +8 -5
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +19 -5
- data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
- data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -2
- data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -4
- data/lib/doorkeeper/oauth/pre_authorization.rb +76 -38
- data/lib/doorkeeper/oauth/refresh_token_request.rb +17 -14
- data/lib/doorkeeper/oauth/token.rb +2 -2
- data/lib/doorkeeper/oauth/token_introspection.rb +26 -16
- data/lib/doorkeeper/oauth/token_request.rb +4 -18
- data/lib/doorkeeper/orm/active_record/access_grant.rb +4 -43
- data/lib/doorkeeper/orm/active_record/access_token.rb +4 -35
- data/lib/doorkeeper/orm/active_record/application.rb +5 -83
- data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +53 -0
- data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +47 -0
- data/lib/doorkeeper/orm/active_record/mixins/application.rb +128 -0
- data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +61 -0
- data/lib/doorkeeper/orm/active_record.rb +21 -6
- data/lib/doorkeeper/rails/helpers.rb +4 -4
- data/lib/doorkeeper/rails/routes.rb +5 -7
- data/lib/doorkeeper/rake/db.rake +3 -3
- data/lib/doorkeeper/request/authorization_code.rb +5 -3
- data/lib/doorkeeper/request/client_credentials.rb +2 -2
- data/lib/doorkeeper/request/password.rb +2 -2
- data/lib/doorkeeper/request/refresh_token.rb +3 -3
- data/lib/doorkeeper/request.rb +7 -12
- data/lib/doorkeeper/server.rb +3 -7
- data/lib/doorkeeper/stale_records_cleaner.rb +6 -2
- data/lib/doorkeeper/version.rb +1 -1
- data/lib/doorkeeper.rb +6 -3
- data/lib/generators/doorkeeper/application_owner_generator.rb +1 -1
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +1 -1
- data/lib/generators/doorkeeper/migration_generator.rb +1 -1
- data/lib/generators/doorkeeper/pkce_generator.rb +1 -1
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +7 -7
- data/lib/generators/doorkeeper/templates/initializer.rb +149 -33
- data/lib/generators/doorkeeper/templates/migration.rb.erb +4 -1
- data/spec/controllers/application_metal_controller_spec.rb +1 -1
- data/spec/controllers/applications_controller_spec.rb +95 -1
- data/spec/controllers/authorizations_controller_spec.rb +156 -75
- data/spec/controllers/protected_resources_controller_spec.rb +28 -20
- data/spec/controllers/token_info_controller_spec.rb +1 -1
- data/spec/controllers/tokens_controller_spec.rb +206 -38
- data/spec/dummy/config/application.rb +3 -1
- data/spec/dummy/config/initializers/doorkeeper.rb +54 -9
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -3
- data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +1 -1
- data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +1 -1
- data/spec/generators/install_generator_spec.rb +1 -1
- data/spec/generators/previous_refresh_token_generator_spec.rb +2 -2
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
- data/spec/lib/config_spec.rb +104 -7
- data/spec/lib/doorkeeper_spec.rb +1 -1
- data/spec/lib/models/revocable_spec.rb +3 -3
- data/spec/lib/oauth/authorization_code_request_spec.rb +129 -115
- data/spec/lib/oauth/base_request_spec.rb +154 -135
- data/spec/lib/oauth/base_response_spec.rb +27 -29
- data/spec/lib/oauth/client/credentials_spec.rb +1 -1
- data/spec/lib/oauth/client_credentials/creator_spec.rb +43 -3
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +12 -12
- data/spec/lib/oauth/client_credentials/validation_spec.rb +4 -4
- data/spec/lib/oauth/client_credentials_integration_spec.rb +16 -18
- data/spec/lib/oauth/client_credentials_request_spec.rb +78 -80
- data/spec/lib/oauth/client_spec.rb +26 -26
- data/spec/lib/oauth/code_request_spec.rb +34 -35
- data/spec/lib/oauth/code_response_spec.rb +21 -25
- data/spec/lib/oauth/error_response_spec.rb +42 -44
- data/spec/lib/oauth/error_spec.rb +12 -14
- data/spec/lib/oauth/forbidden_token_response_spec.rb +11 -13
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +30 -18
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +17 -2
- data/spec/lib/oauth/invalid_request_response_spec.rb +73 -0
- data/spec/lib/oauth/invalid_token_response_spec.rb +32 -34
- data/spec/lib/oauth/password_access_token_request_spec.rb +145 -147
- data/spec/lib/oauth/pre_authorization_spec.rb +161 -153
- data/spec/lib/oauth/refresh_token_request_spec.rb +138 -138
- data/spec/lib/oauth/scopes_spec.rb +104 -106
- data/spec/lib/oauth/token_request_spec.rb +117 -110
- data/spec/lib/oauth/token_response_spec.rb +71 -73
- data/spec/lib/oauth/token_spec.rb +121 -123
- data/spec/lib/server_spec.rb +0 -12
- data/spec/models/doorkeeper/access_grant_spec.rb +21 -4
- data/spec/models/doorkeeper/access_token_spec.rb +42 -11
- data/spec/models/doorkeeper/application_spec.rb +114 -77
- data/spec/requests/applications/applications_request_spec.rb +1 -1
- data/spec/requests/endpoints/authorization_spec.rb +26 -8
- data/spec/requests/endpoints/token_spec.rb +1 -1
- data/spec/requests/flows/authorization_code_errors_spec.rb +1 -0
- data/spec/requests/flows/authorization_code_spec.rb +123 -45
- data/spec/requests/flows/client_credentials_spec.rb +38 -0
- data/spec/requests/flows/implicit_grant_errors_spec.rb +22 -10
- data/spec/requests/flows/implicit_grant_spec.rb +9 -8
- data/spec/requests/flows/password_spec.rb +67 -10
- data/spec/requests/flows/refresh_token_spec.rb +20 -20
- data/spec/requests/flows/revoke_token_spec.rb +37 -23
- data/spec/spec_helper.rb +1 -4
- data/spec/support/doorkeeper_rspec.rb +1 -1
- data/spec/support/helpers/request_spec_helper.rb +14 -2
- data/spec/support/shared/controllers_shared_context.rb +33 -23
- data/spec/validators/redirect_uri_validator_spec.rb +41 -16
- metadata +19 -16
- 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
- data/app/validators/redirect_uri_validator.rb +0 -50
- data/spec/support/http_method_shim.rb +0 -29
|
@@ -3,38 +3,146 @@
|
|
|
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"
|
|
34
142
|
allow(I18n).to receive(:translate)
|
|
35
143
|
.with(
|
|
36
144
|
custom_message,
|
|
37
|
-
hash_including(scope: %i[doorkeeper errors messages])
|
|
145
|
+
hash_including(scope: %i[doorkeeper errors messages]),
|
|
38
146
|
)
|
|
39
147
|
.and_return("Authorization custom message")
|
|
40
148
|
|
|
@@ -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
|
|
@@ -25,14 +25,14 @@ class CreateDoorkeeperTables < ActiveRecord::Migration[4.2]
|
|
|
25
25
|
t.text :redirect_uri, null: false
|
|
26
26
|
t.datetime :created_at, null: false
|
|
27
27
|
t.datetime :revoked_at
|
|
28
|
-
t.string :scopes
|
|
28
|
+
t.string :scopes, null: false, default: ""
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
add_index :oauth_access_grants, :token, unique: true
|
|
32
32
|
add_foreign_key(
|
|
33
33
|
:oauth_access_grants,
|
|
34
34
|
:oauth_applications,
|
|
35
|
-
column: :application_id
|
|
35
|
+
column: :application_id,
|
|
36
36
|
)
|
|
37
37
|
|
|
38
38
|
create_table :oauth_access_tokens do |t|
|
|
@@ -59,7 +59,7 @@ class CreateDoorkeeperTables < ActiveRecord::Migration[4.2]
|
|
|
59
59
|
add_foreign_key(
|
|
60
60
|
:oauth_access_tokens,
|
|
61
61
|
:oauth_applications,
|
|
62
|
-
column: :application_id
|
|
62
|
+
column: :application_id,
|
|
63
63
|
)
|
|
64
64
|
|
|
65
65
|
# Uncomment below to ensure a valid reference to the resource owner's table
|
|
@@ -7,7 +7,7 @@ class AddConfidentialToApplications < ActiveRecord::Migration[5.1]
|
|
|
7
7
|
:confidential,
|
|
8
8
|
:boolean,
|
|
9
9
|
null: false,
|
|
10
|
-
default: true # maintaining backwards compatibility: require secrets
|
|
10
|
+
default: true, # maintaining backwards compatibility: require secrets
|
|
11
11
|
)
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -16,7 +16,7 @@ describe "Doorkeeper::InstallGenerator" do
|
|
|
16
16
|
FileUtils.mkdir(::File.expand_path("db", Pathname(destination_root)))
|
|
17
17
|
FileUtils.copy_file(
|
|
18
18
|
::File.expand_path("../templates/routes.rb", __FILE__),
|
|
19
|
-
::File.expand_path("config/routes.rb", Pathname.new(destination_root))
|
|
19
|
+
::File.expand_path("config/routes.rb", Pathname.new(destination_root)),
|
|
20
20
|
)
|
|
21
21
|
run_generator
|
|
22
22
|
end
|
|
@@ -14,7 +14,7 @@ describe "Doorkeeper::PreviousRefreshTokenGenerator" do
|
|
|
14
14
|
prepare_destination
|
|
15
15
|
|
|
16
16
|
allow_any_instance_of(Doorkeeper::PreviousRefreshTokenGenerator).to(
|
|
17
|
-
receive(:no_previous_refresh_token_column?).and_return(true)
|
|
17
|
+
receive(:no_previous_refresh_token_column?).and_return(true),
|
|
18
18
|
)
|
|
19
19
|
end
|
|
20
20
|
|
|
@@ -32,7 +32,7 @@ describe "Doorkeeper::PreviousRefreshTokenGenerator" do
|
|
|
32
32
|
context "already exist" do
|
|
33
33
|
it "does not create a migration" do
|
|
34
34
|
allow_any_instance_of(Doorkeeper::PreviousRefreshTokenGenerator).to(
|
|
35
|
-
receive(:no_previous_refresh_token_column?).and_call_original
|
|
35
|
+
receive(:no_previous_refresh_token_column?).and_call_original,
|
|
36
36
|
)
|
|
37
37
|
|
|
38
38
|
run_generator
|
data/spec/lib/config_spec.rb
CHANGED
|
@@ -22,7 +22,7 @@ describe Doorkeeper, "configuration" do
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
expect(Rails.logger).to receive(:warn).with(
|
|
25
|
-
I18n.t("doorkeeper.errors.messages.resource_owner_authenticator_not_configured")
|
|
25
|
+
I18n.t("doorkeeper.errors.messages.resource_owner_authenticator_not_configured"),
|
|
26
26
|
)
|
|
27
27
|
subject.authenticate_resource_owner.call(nil)
|
|
28
28
|
end
|
|
@@ -45,7 +45,7 @@ describe Doorkeeper, "configuration" do
|
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
expect(Rails.logger).to receive(:warn).with(
|
|
48
|
-
I18n.t("doorkeeper.errors.messages.credential_flow_not_configured")
|
|
48
|
+
I18n.t("doorkeeper.errors.messages.credential_flow_not_configured"),
|
|
49
49
|
)
|
|
50
50
|
subject.resource_owner_from_credentials.call(nil)
|
|
51
51
|
end
|
|
@@ -471,7 +471,7 @@ describe Doorkeeper, "configuration" do
|
|
|
471
471
|
describe "access_token_generator" do
|
|
472
472
|
it "is 'Doorkeeper::OAuth::Helpers::UniqueToken' by default" do
|
|
473
473
|
expect(Doorkeeper.configuration.access_token_generator).to(
|
|
474
|
-
eq("Doorkeeper::OAuth::Helpers::UniqueToken")
|
|
474
|
+
eq("Doorkeeper::OAuth::Helpers::UniqueToken"),
|
|
475
475
|
)
|
|
476
476
|
end
|
|
477
477
|
|
|
@@ -502,7 +502,21 @@ describe Doorkeeper, "configuration" do
|
|
|
502
502
|
|
|
503
503
|
describe "base_controller" do
|
|
504
504
|
context "default" do
|
|
505
|
-
it { expect(Doorkeeper.configuration.base_controller).to
|
|
505
|
+
it { expect(Doorkeeper.configuration.base_controller).to be_an_instance_of(Proc) }
|
|
506
|
+
|
|
507
|
+
it "resolves to a ApplicationController::Base in default mode" do
|
|
508
|
+
expect(Doorkeeper.configuration.resolve_controller(:base))
|
|
509
|
+
.to eq(ActionController::Base)
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
it "resolves to a ApplicationController::API in api_only mode" do
|
|
513
|
+
Doorkeeper.configure do
|
|
514
|
+
api_only
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
expect(Doorkeeper.configuration.resolve_controller(:base))
|
|
518
|
+
.to eq(ActionController::API)
|
|
519
|
+
end
|
|
506
520
|
end
|
|
507
521
|
|
|
508
522
|
context "custom" do
|
|
@@ -517,7 +531,26 @@ describe Doorkeeper, "configuration" do
|
|
|
517
531
|
end
|
|
518
532
|
end
|
|
519
533
|
|
|
534
|
+
describe "base_metal_controller" do
|
|
535
|
+
context "default" do
|
|
536
|
+
it { expect(Doorkeeper.configuration.base_metal_controller).to eq("ActionController::API") }
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
context "custom" do
|
|
540
|
+
before do
|
|
541
|
+
Doorkeeper.configure do
|
|
542
|
+
orm DOORKEEPER_ORM
|
|
543
|
+
base_metal_controller { "ApplicationController" }
|
|
544
|
+
end
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
it { expect(Doorkeeper.configuration.resolve_controller(:base_metal)).to eq(ApplicationController) }
|
|
548
|
+
end
|
|
549
|
+
end
|
|
550
|
+
|
|
520
551
|
if DOORKEEPER_ORM == :active_record
|
|
552
|
+
class FakeCustomModel; end
|
|
553
|
+
|
|
521
554
|
describe "active_record_options" do
|
|
522
555
|
let(:models) { [Doorkeeper::AccessGrant, Doorkeeper::AccessToken, Doorkeeper::Application] }
|
|
523
556
|
|
|
@@ -535,11 +568,62 @@ describe Doorkeeper, "configuration" do
|
|
|
535
568
|
Doorkeeper.configure do
|
|
536
569
|
orm DOORKEEPER_ORM
|
|
537
570
|
active_record_options(
|
|
538
|
-
establish_connection: Rails.configuration.database_configuration[Rails.env]
|
|
571
|
+
establish_connection: Rails.configuration.database_configuration[Rails.env],
|
|
539
572
|
)
|
|
540
573
|
end
|
|
541
574
|
end
|
|
542
575
|
end
|
|
576
|
+
|
|
577
|
+
describe "access_token_class" do
|
|
578
|
+
it "uses default doorkeeper value" do
|
|
579
|
+
expect(subject.access_token_class).to eq("Doorkeeper::AccessToken")
|
|
580
|
+
expect(subject.access_token_model).to be(Doorkeeper::AccessToken)
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
it "can change the value" do
|
|
584
|
+
Doorkeeper.configure do
|
|
585
|
+
orm DOORKEEPER_ORM
|
|
586
|
+
access_token_class "FakeCustomModel"
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
expect(subject.access_token_class).to eq("FakeCustomModel")
|
|
590
|
+
expect(subject.access_token_model).to be(FakeCustomModel)
|
|
591
|
+
end
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
describe "access_grant_class" do
|
|
595
|
+
it "uses default doorkeeper value" do
|
|
596
|
+
expect(subject.access_grant_class).to eq("Doorkeeper::AccessGrant")
|
|
597
|
+
expect(subject.access_grant_model).to be(Doorkeeper::AccessGrant)
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
it "can change the value" do
|
|
601
|
+
Doorkeeper.configure do
|
|
602
|
+
orm DOORKEEPER_ORM
|
|
603
|
+
access_grant_class "FakeCustomModel"
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
expect(subject.access_grant_class).to eq("FakeCustomModel")
|
|
607
|
+
expect(subject.access_grant_model).to be(FakeCustomModel)
|
|
608
|
+
end
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
describe "application_class" do
|
|
612
|
+
it "uses default doorkeeper value" do
|
|
613
|
+
expect(subject.application_class).to eq("Doorkeeper::Application")
|
|
614
|
+
expect(subject.application_model).to be(Doorkeeper::Application)
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
it "can change the value" do
|
|
618
|
+
Doorkeeper.configure do
|
|
619
|
+
orm DOORKEEPER_ORM
|
|
620
|
+
application_class "FakeCustomModel"
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
expect(subject.application_class).to eq("FakeCustomModel")
|
|
624
|
+
expect(subject.application_model).to be(FakeCustomModel)
|
|
625
|
+
end
|
|
626
|
+
end
|
|
543
627
|
end
|
|
544
628
|
|
|
545
629
|
describe "api_only" do
|
|
@@ -620,8 +704,10 @@ describe Doorkeeper, "configuration" do
|
|
|
620
704
|
Doorkeeper.configure do
|
|
621
705
|
hash_token_secrets using: "Doorkeeper::SecretStoring::BCrypt"
|
|
622
706
|
end
|
|
623
|
-
end.to raise_error(
|
|
624
|
-
|
|
707
|
+
end.to raise_error(
|
|
708
|
+
ArgumentError,
|
|
709
|
+
/can only be used for storing application secrets/,
|
|
710
|
+
)
|
|
625
711
|
end
|
|
626
712
|
end
|
|
627
713
|
|
|
@@ -694,4 +780,15 @@ describe Doorkeeper, "configuration" do
|
|
|
694
780
|
end
|
|
695
781
|
end
|
|
696
782
|
end
|
|
783
|
+
|
|
784
|
+
describe "options deprecation" do
|
|
785
|
+
it "prints a warning message when an option is deprecated" do
|
|
786
|
+
expect(Kernel).to receive(:warn).with(
|
|
787
|
+
"[DOORKEEPER] native_redirect_uri has been deprecated and will soon be removed",
|
|
788
|
+
)
|
|
789
|
+
Doorkeeper.configure do
|
|
790
|
+
native_redirect_uri "urn:ietf:wg:oauth:2.0:oob"
|
|
791
|
+
end
|
|
792
|
+
end
|
|
793
|
+
end
|
|
697
794
|
end
|
data/spec/lib/doorkeeper_spec.rb
CHANGED
|
@@ -7,7 +7,7 @@ describe Doorkeeper do
|
|
|
7
7
|
let(:request) { double }
|
|
8
8
|
|
|
9
9
|
it "calls OAuth::Token#authenticate" do
|
|
10
|
-
token_strategies = Doorkeeper.
|
|
10
|
+
token_strategies = Doorkeeper.config.access_token_methods
|
|
11
11
|
|
|
12
12
|
expect(Doorkeeper::OAuth::Token).to receive(:authenticate)
|
|
13
13
|
.with(request, *token_strategies)
|