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
|
@@ -40,6 +40,21 @@ module Doorkeeper
|
|
|
40
40
|
find_by_plaintext_token(:refresh_token, refresh_token)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
# Returns an instance of the Doorkeeper::AccessToken
|
|
44
|
+
# found by previous refresh token. Keep in mind that value
|
|
45
|
+
# of the previous_refresh_token isn't encrypted using
|
|
46
|
+
# secrets strategy.
|
|
47
|
+
#
|
|
48
|
+
# @param previous_refresh_token [#to_s]
|
|
49
|
+
# previous refresh token value (any object that responds to `#to_s`)
|
|
50
|
+
#
|
|
51
|
+
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
|
|
52
|
+
# if there is no record with such refresh token
|
|
53
|
+
#
|
|
54
|
+
def by_previous_refresh_token(previous_refresh_token)
|
|
55
|
+
find_by(refresh_token: previous_refresh_token)
|
|
56
|
+
end
|
|
57
|
+
|
|
43
58
|
# Revokes AccessToken records that have not been revoked and associated
|
|
44
59
|
# with the specific Application and Resource Owner.
|
|
45
60
|
#
|
|
@@ -49,10 +64,11 @@ module Doorkeeper
|
|
|
49
64
|
# instance of the Resource Owner model
|
|
50
65
|
#
|
|
51
66
|
def revoke_all_for(application_id, resource_owner, clock = Time)
|
|
52
|
-
where(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
67
|
+
where(
|
|
68
|
+
application_id: application_id,
|
|
69
|
+
resource_owner_id: resource_owner.id,
|
|
70
|
+
revoked_at: nil,
|
|
71
|
+
).update_all(revoked_at: clock.now.utc)
|
|
56
72
|
end
|
|
57
73
|
|
|
58
74
|
# Looking for not revoked Access Token with a matching set of scopes
|
|
@@ -76,9 +92,50 @@ module Doorkeeper
|
|
|
76
92
|
end
|
|
77
93
|
|
|
78
94
|
tokens = authorized_tokens_for(application.try(:id), resource_owner_id)
|
|
79
|
-
tokens
|
|
80
|
-
|
|
95
|
+
find_matching_token(tokens, application, scopes)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Interface to enumerate access token records in batches in order not
|
|
99
|
+
# to bloat the memory. Could be overloaded in any ORM extension.
|
|
100
|
+
#
|
|
101
|
+
def find_access_token_in_batches(relation, *args, &block)
|
|
102
|
+
relation.find_in_batches(*args, &block)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Enumerates AccessToken records in batches to find a matching token.
|
|
106
|
+
# Batching is required in order not to pollute the memory if Application
|
|
107
|
+
# has huge amount of associated records.
|
|
108
|
+
#
|
|
109
|
+
# ActiveRecord 5.x - 6.x ignores custom ordering so we can't perform a
|
|
110
|
+
# database sort by created_at, so we need to load all the matching records,
|
|
111
|
+
# sort them and find latest one. Probably it would be better to rewrite this
|
|
112
|
+
# query using Time math if possible, but we n eed to consider ORM and
|
|
113
|
+
# different databases support.
|
|
114
|
+
#
|
|
115
|
+
# @param relation [ActiveRecord::Relation]
|
|
116
|
+
# Access tokens relation
|
|
117
|
+
# @param application [Doorkeeper::Application]
|
|
118
|
+
# Application instance
|
|
119
|
+
# @param scopes [String, Doorkeeper::OAuth::Scopes]
|
|
120
|
+
# set of scopes
|
|
121
|
+
#
|
|
122
|
+
# @return [Doorkeeper::AccessToken, nil] Access Token instance or
|
|
123
|
+
# nil if matching record was not found
|
|
124
|
+
#
|
|
125
|
+
def find_matching_token(relation, application, scopes)
|
|
126
|
+
return nil unless relation
|
|
127
|
+
|
|
128
|
+
matching_tokens = []
|
|
129
|
+
|
|
130
|
+
find_access_token_in_batches(relation) do |batch|
|
|
131
|
+
tokens = batch.select do |token|
|
|
132
|
+
scopes_match?(token.scopes, scopes, application.try(:scopes))
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
matching_tokens.concat(tokens)
|
|
81
136
|
end
|
|
137
|
+
|
|
138
|
+
matching_tokens.max_by(&:created_at)
|
|
82
139
|
end
|
|
83
140
|
|
|
84
141
|
# Checks whether the token scopes match the scopes from the parameters
|
|
@@ -101,8 +158,8 @@ module Doorkeeper
|
|
|
101
158
|
(token_scopes.sort == param_scopes.sort) &&
|
|
102
159
|
Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
|
|
103
160
|
scope_str: param_scopes.to_s,
|
|
104
|
-
server_scopes: Doorkeeper.
|
|
105
|
-
app_scopes: app_scopes
|
|
161
|
+
server_scopes: Doorkeeper.config.scopes,
|
|
162
|
+
app_scopes: app_scopes,
|
|
106
163
|
)
|
|
107
164
|
end
|
|
108
165
|
|
|
@@ -124,7 +181,7 @@ module Doorkeeper
|
|
|
124
181
|
# @return [Doorkeeper::AccessToken] existing record or a new one
|
|
125
182
|
#
|
|
126
183
|
def find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token)
|
|
127
|
-
if Doorkeeper.
|
|
184
|
+
if Doorkeeper.config.reuse_access_token
|
|
128
185
|
access_token = matching_token_for(application, resource_owner_id, scopes)
|
|
129
186
|
|
|
130
187
|
return access_token if access_token&.reusable?
|
|
@@ -135,7 +192,7 @@ module Doorkeeper
|
|
|
135
192
|
resource_owner_id: resource_owner_id,
|
|
136
193
|
scopes: scopes.to_s,
|
|
137
194
|
expires_in: expires_in,
|
|
138
|
-
use_refresh_token: use_refresh_token
|
|
195
|
+
use_refresh_token: use_refresh_token,
|
|
139
196
|
)
|
|
140
197
|
end
|
|
141
198
|
|
|
@@ -150,10 +207,11 @@ module Doorkeeper
|
|
|
150
207
|
# @return [Doorkeeper::AccessToken] array of matching AccessToken objects
|
|
151
208
|
#
|
|
152
209
|
def authorized_tokens_for(application_id, resource_owner_id)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
210
|
+
where(
|
|
211
|
+
application_id: application_id,
|
|
212
|
+
resource_owner_id: resource_owner_id,
|
|
213
|
+
revoked_at: nil,
|
|
214
|
+
)
|
|
157
215
|
end
|
|
158
216
|
|
|
159
217
|
# Convenience method for backwards-compatibility, return the last
|
|
@@ -168,21 +226,22 @@ module Doorkeeper
|
|
|
168
226
|
# nil if nothing was found
|
|
169
227
|
#
|
|
170
228
|
def last_authorized_token_for(application_id, resource_owner_id)
|
|
171
|
-
authorized_tokens_for(application_id, resource_owner_id)
|
|
229
|
+
authorized_tokens_for(application_id, resource_owner_id)
|
|
230
|
+
.ordered_by(:created_at, :desc).first
|
|
172
231
|
end
|
|
173
232
|
|
|
174
233
|
##
|
|
175
234
|
# Determines the secret storing transformer
|
|
176
235
|
# Unless configured otherwise, uses the plain secret strategy
|
|
177
236
|
def secret_strategy
|
|
178
|
-
::Doorkeeper.
|
|
237
|
+
::Doorkeeper.config.token_secret_strategy
|
|
179
238
|
end
|
|
180
239
|
|
|
181
240
|
##
|
|
182
241
|
# Determine the fallback storing strategy
|
|
183
242
|
# Unless configured, there will be no fallback
|
|
184
243
|
def fallback_secret_strategy
|
|
185
|
-
::Doorkeeper.
|
|
244
|
+
::Doorkeeper.config.token_secret_fallback_strategy
|
|
186
245
|
end
|
|
187
246
|
end
|
|
188
247
|
|
|
@@ -259,8 +318,28 @@ module Doorkeeper
|
|
|
259
318
|
end
|
|
260
319
|
end
|
|
261
320
|
|
|
321
|
+
# Revokes token with `:refresh_token` equal to `:previous_refresh_token`
|
|
322
|
+
# and clears `:previous_refresh_token` attribute.
|
|
323
|
+
#
|
|
324
|
+
def revoke_previous_refresh_token!
|
|
325
|
+
return unless self.class.refresh_token_revoked_on_use?
|
|
326
|
+
|
|
327
|
+
old_refresh_token&.revoke
|
|
328
|
+
update_attribute :previous_refresh_token, ""
|
|
329
|
+
end
|
|
330
|
+
|
|
262
331
|
private
|
|
263
332
|
|
|
333
|
+
# Searches for Access Token record with `:refresh_token` equal to
|
|
334
|
+
# `:previous_refresh_token` value.
|
|
335
|
+
#
|
|
336
|
+
# @return [Doorkeeper::AccessToken, nil]
|
|
337
|
+
# Access Token record or nil if nothing found
|
|
338
|
+
#
|
|
339
|
+
def old_refresh_token
|
|
340
|
+
@old_refresh_token ||= self.class.by_previous_refresh_token(previous_refresh_token)
|
|
341
|
+
end
|
|
342
|
+
|
|
264
343
|
# Generates refresh token with UniqueToken generator.
|
|
265
344
|
#
|
|
266
345
|
# @return [String] refresh token value
|
|
@@ -271,7 +350,7 @@ module Doorkeeper
|
|
|
271
350
|
end
|
|
272
351
|
|
|
273
352
|
# Generates and sets the token value with the
|
|
274
|
-
# configured Generator class (see Doorkeeper.
|
|
353
|
+
# configured Generator class (see Doorkeeper.config).
|
|
275
354
|
#
|
|
276
355
|
# @return [String] generated token value
|
|
277
356
|
#
|
|
@@ -288,7 +367,7 @@ module Doorkeeper
|
|
|
288
367
|
scopes: scopes,
|
|
289
368
|
application: application,
|
|
290
369
|
expires_in: expires_in,
|
|
291
|
-
created_at: created_at
|
|
370
|
+
created_at: created_at,
|
|
292
371
|
)
|
|
293
372
|
|
|
294
373
|
secret_strategy.store_secret(self, :token, @raw_token)
|
|
@@ -296,7 +375,7 @@ module Doorkeeper
|
|
|
296
375
|
end
|
|
297
376
|
|
|
298
377
|
def token_generator
|
|
299
|
-
generator_name = Doorkeeper.
|
|
378
|
+
generator_name = Doorkeeper.config.access_token_generator
|
|
300
379
|
generator = generator_name.constantize
|
|
301
380
|
|
|
302
381
|
return generator if generator.respond_to?(:generate)
|
|
@@ -47,14 +47,14 @@ module Doorkeeper
|
|
|
47
47
|
# Determines the secret storing transformer
|
|
48
48
|
# Unless configured otherwise, uses the plain secret strategy
|
|
49
49
|
def secret_strategy
|
|
50
|
-
::Doorkeeper.
|
|
50
|
+
::Doorkeeper.config.application_secret_strategy
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
##
|
|
54
54
|
# Determine the fallback storing strategy
|
|
55
55
|
# Unless configured, there will be no fallback
|
|
56
56
|
def fallback_secret_strategy
|
|
57
|
-
::Doorkeeper.
|
|
57
|
+
::Doorkeeper.config.application_secret_fallback_strategy
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
|
|
@@ -72,7 +72,7 @@ module Doorkeeper
|
|
|
72
72
|
# @param input [#to_s] Plain secret provided by user
|
|
73
73
|
# (any object that responds to `#to_s`)
|
|
74
74
|
#
|
|
75
|
-
# @return [
|
|
75
|
+
# @return [Boolean] Whether the given secret matches the stored secret
|
|
76
76
|
# of this application.
|
|
77
77
|
#
|
|
78
78
|
def secret_matches?(input)
|
|
@@ -11,7 +11,7 @@ module Doorkeeper
|
|
|
11
11
|
return false if expired?
|
|
12
12
|
return true unless expires_in
|
|
13
13
|
|
|
14
|
-
threshold_limit = 100 - Doorkeeper.
|
|
14
|
+
threshold_limit = 100 - Doorkeeper.config.token_reuse_limit
|
|
15
15
|
expires_in_seconds >= threshold_limit * expires_in / 100
|
|
16
16
|
end
|
|
17
17
|
end
|
|
@@ -19,33 +19,6 @@ module Doorkeeper
|
|
|
19
19
|
def revoked?
|
|
20
20
|
!!(revoked_at && revoked_at <= Time.now.utc)
|
|
21
21
|
end
|
|
22
|
-
|
|
23
|
-
# Revokes token with `:refresh_token` equal to `:previous_refresh_token`
|
|
24
|
-
# and clears `:previous_refresh_token` attribute.
|
|
25
|
-
#
|
|
26
|
-
def revoke_previous_refresh_token!
|
|
27
|
-
return unless refresh_token_revoked_on_use?
|
|
28
|
-
|
|
29
|
-
old_refresh_token&.revoke
|
|
30
|
-
update_attribute :previous_refresh_token, ""
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
private
|
|
34
|
-
|
|
35
|
-
# Searches for Access Token record with `:refresh_token` equal to
|
|
36
|
-
# `:previous_refresh_token` value.
|
|
37
|
-
#
|
|
38
|
-
# @return [Doorkeeper::AccessToken, nil]
|
|
39
|
-
# Access Token record or nil if nothing found
|
|
40
|
-
#
|
|
41
|
-
def old_refresh_token
|
|
42
|
-
@old_refresh_token ||=
|
|
43
|
-
AccessToken.by_refresh_token(previous_refresh_token)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def refresh_token_revoked_on_use?
|
|
47
|
-
AccessToken.refresh_token_revoked_on_use?
|
|
48
|
-
end
|
|
49
22
|
end
|
|
50
23
|
end
|
|
51
24
|
end
|
|
@@ -12,29 +12,27 @@ module Doorkeeper
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def issue_token
|
|
15
|
-
@token ||=
|
|
15
|
+
@token ||= Doorkeeper.config.access_grant_model.create!(access_grant_attributes)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def
|
|
18
|
+
def oob_redirect
|
|
19
19
|
{ action: :show, code: token.plaintext_token }
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def configuration
|
|
23
|
-
Doorkeeper.configuration
|
|
24
|
-
end
|
|
25
|
-
|
|
26
22
|
private
|
|
27
23
|
|
|
28
24
|
def authorization_code_expires_in
|
|
29
|
-
|
|
25
|
+
Doorkeeper.config.authorization_code_expires_in
|
|
30
26
|
end
|
|
31
27
|
|
|
32
28
|
def access_grant_attributes
|
|
33
|
-
pkce_attributes.merge
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
pkce_attributes.merge(
|
|
30
|
+
application_id: pre_auth.client.id,
|
|
31
|
+
resource_owner_id: resource_owner.id,
|
|
32
|
+
expires_in: authorization_code_expires_in,
|
|
33
|
+
redirect_uri: pre_auth.redirect_uri,
|
|
34
|
+
scopes: pre_auth.scopes.to_s,
|
|
35
|
+
)
|
|
38
36
|
end
|
|
39
37
|
|
|
40
38
|
def pkce_attributes
|
|
@@ -46,10 +44,10 @@ module Doorkeeper
|
|
|
46
44
|
}
|
|
47
45
|
end
|
|
48
46
|
|
|
49
|
-
#
|
|
47
|
+
# Ensures firstly, if migration with additional PKCE columns was
|
|
50
48
|
# generated and migrated
|
|
51
49
|
def pkce_supported?
|
|
52
|
-
Doorkeeper
|
|
50
|
+
Doorkeeper.config.access_grant_model.pkce_supported?
|
|
53
51
|
end
|
|
54
52
|
end
|
|
55
53
|
end
|
|
@@ -19,7 +19,7 @@ module Doorkeeper
|
|
|
19
19
|
Doorkeeper::OAuth::Authorization::Context.new(
|
|
20
20
|
oauth_client,
|
|
21
21
|
grant_type,
|
|
22
|
-
scopes
|
|
22
|
+
scopes,
|
|
23
23
|
)
|
|
24
24
|
end
|
|
25
25
|
|
|
@@ -35,7 +35,7 @@ module Doorkeeper
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def refresh_token_enabled?(server, context)
|
|
38
|
-
if server.refresh_token_enabled?.respond_to?
|
|
38
|
+
if server.refresh_token_enabled?.respond_to?(:call)
|
|
39
39
|
server.refresh_token_enabled?.call(context)
|
|
40
40
|
else
|
|
41
41
|
!!server.refresh_token_enabled?
|
|
@@ -49,21 +49,24 @@ module Doorkeeper
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def issue_token
|
|
52
|
+
return @token if defined?(@token)
|
|
53
|
+
|
|
52
54
|
context = self.class.build_context(
|
|
53
55
|
pre_auth.client,
|
|
54
56
|
Doorkeeper::OAuth::IMPLICIT,
|
|
55
|
-
pre_auth.scopes
|
|
57
|
+
pre_auth.scopes,
|
|
56
58
|
)
|
|
57
|
-
|
|
59
|
+
|
|
60
|
+
@token = configuration.access_token_model.find_or_create_for(
|
|
58
61
|
pre_auth.client,
|
|
59
62
|
resource_owner.id,
|
|
60
63
|
pre_auth.scopes,
|
|
61
64
|
self.class.access_token_expires_in(configuration, context),
|
|
62
|
-
false
|
|
65
|
+
false,
|
|
63
66
|
)
|
|
64
67
|
end
|
|
65
68
|
|
|
66
|
-
def
|
|
69
|
+
def oob_redirect
|
|
67
70
|
{
|
|
68
71
|
controller: controller,
|
|
69
72
|
action: :show,
|
|
@@ -74,7 +77,7 @@ module Doorkeeper
|
|
|
74
77
|
private
|
|
75
78
|
|
|
76
79
|
def configuration
|
|
77
|
-
Doorkeeper.
|
|
80
|
+
Doorkeeper.config
|
|
78
81
|
end
|
|
79
82
|
|
|
80
83
|
def controller
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
module Doorkeeper
|
|
4
4
|
module OAuth
|
|
5
5
|
class AuthorizationCodeRequest < BaseRequest
|
|
6
|
-
validate :
|
|
6
|
+
validate :pkce_support, error: :invalid_request
|
|
7
|
+
validate :params, error: :invalid_request
|
|
7
8
|
validate :client, error: :invalid_client
|
|
8
9
|
validate :grant, error: :invalid_grant
|
|
9
10
|
# @see https://tools.ietf.org/html/rfc6749#section-5.2
|
|
@@ -12,6 +13,7 @@ module Doorkeeper
|
|
|
12
13
|
|
|
13
14
|
attr_accessor :server, :grant, :client, :redirect_uri, :access_token,
|
|
14
15
|
:code_verifier
|
|
16
|
+
attr_reader :invalid_request_reason, :missing_param
|
|
15
17
|
|
|
16
18
|
def initialize(server, grant, client, parameters = {})
|
|
17
19
|
@server = server
|
|
@@ -24,29 +26,39 @@ module Doorkeeper
|
|
|
24
26
|
|
|
25
27
|
private
|
|
26
28
|
|
|
27
|
-
def client_by_uid(parameters)
|
|
28
|
-
Doorkeeper::Application.by_uid(parameters[:client_id])
|
|
29
|
-
end
|
|
30
|
-
|
|
31
29
|
def before_successful_response
|
|
32
30
|
grant.transaction do
|
|
33
31
|
grant.lock!
|
|
34
32
|
raise Errors::InvalidGrantReuse if grant.revoked?
|
|
35
33
|
|
|
36
34
|
grant.revoke
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
|
|
36
|
+
find_or_create_access_token(
|
|
37
|
+
grant.application,
|
|
38
|
+
grant.resource_owner_id,
|
|
39
|
+
grant.scopes,
|
|
40
|
+
server,
|
|
41
|
+
)
|
|
41
42
|
end
|
|
42
43
|
super
|
|
43
44
|
end
|
|
44
45
|
|
|
45
|
-
def
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
def validate_pkce_support
|
|
47
|
+
@invalid_request_reason = :not_support_pkce if grant &&
|
|
48
|
+
!grant.pkce_supported? &&
|
|
49
|
+
code_verifier.present?
|
|
50
|
+
|
|
51
|
+
@invalid_request_reason.nil?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def validate_params
|
|
55
|
+
@missing_param = if grant&.uses_pkce? && code_verifier.blank?
|
|
56
|
+
:code_verifier
|
|
57
|
+
elsif redirect_uri.blank?
|
|
58
|
+
:redirect_uri
|
|
59
|
+
end
|
|
48
60
|
|
|
49
|
-
|
|
61
|
+
@missing_param.nil?
|
|
50
62
|
end
|
|
51
63
|
|
|
52
64
|
def validate_client
|
|
@@ -62,7 +74,7 @@ module Doorkeeper
|
|
|
62
74
|
def validate_redirect_uri
|
|
63
75
|
Helpers::URIChecker.valid_for_authorization?(
|
|
64
76
|
redirect_uri,
|
|
65
|
-
grant.redirect_uri
|
|
77
|
+
grant.redirect_uri,
|
|
66
78
|
)
|
|
67
79
|
end
|
|
68
80
|
|
|
@@ -73,13 +85,17 @@ module Doorkeeper
|
|
|
73
85
|
return false unless grant.pkce_supported?
|
|
74
86
|
|
|
75
87
|
if grant.code_challenge_method == "S256"
|
|
76
|
-
grant.code_challenge ==
|
|
88
|
+
grant.code_challenge == generate_code_challenge(code_verifier)
|
|
77
89
|
elsif grant.code_challenge_method == "plain"
|
|
78
90
|
grant.code_challenge == code_verifier
|
|
79
91
|
else
|
|
80
92
|
false
|
|
81
93
|
end
|
|
82
94
|
end
|
|
95
|
+
|
|
96
|
+
def generate_code_challenge(code_verifier)
|
|
97
|
+
server_config.access_grant_model.generate_code_challenge(code_verifier)
|
|
98
|
+
end
|
|
83
99
|
end
|
|
84
100
|
end
|
|
85
101
|
end
|
|
@@ -15,6 +15,8 @@ module Doorkeeper
|
|
|
15
15
|
@response = TokenResponse.new(access_token)
|
|
16
16
|
after_successful_response
|
|
17
17
|
@response
|
|
18
|
+
elsif error == :invalid_request
|
|
19
|
+
@response = InvalidRequestResponse.from_request(self)
|
|
18
20
|
else
|
|
19
21
|
@response = ErrorResponse.from_request(self)
|
|
20
22
|
end
|
|
@@ -34,21 +36,25 @@ module Doorkeeper
|
|
|
34
36
|
|
|
35
37
|
def find_or_create_access_token(client, resource_owner_id, scopes, server)
|
|
36
38
|
context = Authorization::Token.build_context(client, grant_type, scopes)
|
|
37
|
-
@access_token =
|
|
39
|
+
@access_token = server_config.access_token_model.find_or_create_for(
|
|
38
40
|
client,
|
|
39
41
|
resource_owner_id,
|
|
40
42
|
scopes,
|
|
41
43
|
Authorization::Token.access_token_expires_in(server, context),
|
|
42
|
-
Authorization::Token.refresh_token_enabled?(server, context)
|
|
44
|
+
Authorization::Token.refresh_token_enabled?(server, context),
|
|
43
45
|
)
|
|
44
46
|
end
|
|
45
47
|
|
|
46
48
|
def before_successful_response
|
|
47
|
-
|
|
49
|
+
server_config.before_successful_strategy_response.call(self)
|
|
48
50
|
end
|
|
49
51
|
|
|
50
52
|
def after_successful_response
|
|
51
|
-
|
|
53
|
+
server_config.after_successful_strategy_response.call(self, @response)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def server_config
|
|
57
|
+
Doorkeeper.config
|
|
52
58
|
end
|
|
53
59
|
|
|
54
60
|
private
|
|
@@ -11,18 +11,17 @@ module Doorkeeper
|
|
|
11
11
|
@application = application
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def self.find(uid, method =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
def self.find(uid, method = Doorkeeper.config.application_model.method(:by_uid))
|
|
15
|
+
return unless (application = method.call(uid))
|
|
16
|
+
|
|
17
|
+
new(application)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def self.authenticate(credentials, method =
|
|
20
|
+
def self.authenticate(credentials, method = Doorkeeper.config.application_model.method(:by_uid_and_secret))
|
|
21
21
|
return if credentials.blank?
|
|
22
|
+
return unless (application = method.call(credentials.uid, credentials.secret))
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
new(application)
|
|
25
|
-
end
|
|
24
|
+
new(application)
|
|
26
25
|
end
|
|
27
26
|
end
|
|
28
27
|
end
|
|
@@ -5,11 +5,32 @@ module Doorkeeper
|
|
|
5
5
|
class ClientCredentialsRequest < BaseRequest
|
|
6
6
|
class Creator
|
|
7
7
|
def call(client, scopes, attributes = {})
|
|
8
|
-
|
|
8
|
+
if lookup_existing_token?
|
|
9
|
+
existing_token = find_existing_token_for(client, scopes)
|
|
10
|
+
return existing_token if server_config.reuse_access_token && existing_token&.reusable?
|
|
11
|
+
|
|
12
|
+
existing_token&.revoke if server_config.revoke_previous_client_credentials_token
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
server_config.access_token_model.find_or_create_for(
|
|
9
16
|
client, nil, scopes, attributes[:expires_in],
|
|
10
|
-
attributes[:use_refresh_token]
|
|
17
|
+
attributes[:use_refresh_token],
|
|
11
18
|
)
|
|
12
19
|
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def lookup_existing_token?
|
|
24
|
+
server_config.reuse_access_token || server_config.revoke_previous_client_credentials_token
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def find_existing_token_for(client, scopes)
|
|
28
|
+
server_config.access_token_model.matching_token_for(client, nil, scopes)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def server_config
|
|
32
|
+
Doorkeeper.config
|
|
33
|
+
end
|
|
13
34
|
end
|
|
14
35
|
end
|
|
15
36
|
end
|
|
@@ -4,20 +4,20 @@ module Doorkeeper
|
|
|
4
4
|
module OAuth
|
|
5
5
|
class ClientCredentialsRequest < BaseRequest
|
|
6
6
|
class Issuer
|
|
7
|
-
attr_accessor :token, :
|
|
7
|
+
attr_accessor :token, :validator, :error
|
|
8
8
|
|
|
9
|
-
def initialize(server,
|
|
9
|
+
def initialize(server, validator)
|
|
10
10
|
@server = server
|
|
11
|
-
@
|
|
11
|
+
@validator = validator
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def create(client, scopes, creator = Creator.new)
|
|
15
|
-
if
|
|
15
|
+
if validator.valid?
|
|
16
16
|
@token = create_token(client, scopes, creator)
|
|
17
17
|
@error = :server_error unless @token
|
|
18
18
|
else
|
|
19
19
|
@token = false
|
|
20
|
-
@error =
|
|
20
|
+
@error = validator.error
|
|
21
21
|
end
|
|
22
22
|
@token
|
|
23
23
|
end
|
|
@@ -28,7 +28,7 @@ module Doorkeeper
|
|
|
28
28
|
context = Authorization::Token.build_context(
|
|
29
29
|
client,
|
|
30
30
|
Doorkeeper::OAuth::CLIENT_CREDENTIALS,
|
|
31
|
-
scopes
|
|
31
|
+
scopes,
|
|
32
32
|
)
|
|
33
33
|
ttl = Authorization::Token.access_token_expires_in(@server, context)
|
|
34
34
|
|
|
@@ -36,7 +36,7 @@ module Doorkeeper
|
|
|
36
36
|
client,
|
|
37
37
|
scopes,
|
|
38
38
|
use_refresh_token: false,
|
|
39
|
-
expires_in: ttl
|
|
39
|
+
expires_in: ttl,
|
|
40
40
|
)
|
|
41
41
|
end
|
|
42
42
|
end
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
module Doorkeeper
|
|
4
4
|
module OAuth
|
|
5
5
|
class ClientCredentialsRequest < BaseRequest
|
|
6
|
-
class
|
|
6
|
+
class Validator
|
|
7
7
|
include Validations
|
|
8
8
|
include OAuth::Helpers
|
|
9
9
|
|
|
10
10
|
validate :client, error: :invalid_client
|
|
11
|
+
validate :client_supports_grant_flow, error: :unauthorized_client
|
|
11
12
|
validate :scopes, error: :invalid_scope
|
|
12
13
|
|
|
13
14
|
def initialize(server, request)
|
|
@@ -24,6 +25,13 @@ module Doorkeeper
|
|
|
24
25
|
@client.present?
|
|
25
26
|
end
|
|
26
27
|
|
|
28
|
+
def validate_client_supports_grant_flow
|
|
29
|
+
Doorkeeper.config.allow_grant_flow_for_client?(
|
|
30
|
+
Doorkeeper::OAuth::CLIENT_CREDENTIALS,
|
|
31
|
+
@client,
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
27
35
|
def validate_scopes
|
|
28
36
|
return true if @request.scopes.blank?
|
|
29
37
|
|
|
@@ -37,7 +45,7 @@ module Doorkeeper
|
|
|
37
45
|
scope_str: @request.scopes.to_s,
|
|
38
46
|
server_scopes: @server.scopes,
|
|
39
47
|
app_scopes: application_scopes,
|
|
40
|
-
grant_type: Doorkeeper::OAuth::CLIENT_CREDENTIALS
|
|
48
|
+
grant_type: Doorkeeper::OAuth::CLIENT_CREDENTIALS,
|
|
41
49
|
)
|
|
42
50
|
end
|
|
43
51
|
end
|