doorkeeper 5.0.3 → 5.1.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/.hound.yml +2 -1
- data/.rubocop.yml +37 -4
- data/.travis.yml +11 -21
- data/Appraisals +29 -7
- data/Dangerfile +5 -2
- data/Gemfile +19 -5
- data/NEWS.md +81 -21
- data/README.md +75 -485
- data/Rakefile +10 -8
- data/app/controllers/doorkeeper/application_controller.rb +1 -2
- data/app/controllers/doorkeeper/application_metal_controller.rb +2 -13
- data/app/controllers/doorkeeper/applications_controller.rb +20 -8
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -1
- data/app/controllers/doorkeeper/token_info_controller.rb +2 -2
- data/app/controllers/doorkeeper/tokens_controller.rb +11 -11
- data/app/helpers/doorkeeper/dashboard_helper.rb +1 -1
- data/app/validators/redirect_uri_validator.rb +5 -2
- data/app/views/doorkeeper/applications/_form.html.erb +6 -0
- data/app/views/doorkeeper/applications/show.html.erb +1 -1
- data/app/views/layouts/doorkeeper/admin.html.erb +5 -3
- data/bin/console +16 -0
- data/config/locales/en.yml +1 -0
- data/doorkeeper.gemspec +24 -21
- data/gemfiles/rails_5_0.gemfile +9 -4
- data/gemfiles/rails_5_1.gemfile +9 -4
- data/gemfiles/rails_5_2.gemfile +10 -5
- data/gemfiles/rails_6_0.gemfile +17 -0
- data/gemfiles/rails_master.gemfile +9 -9
- data/lib/doorkeeper/config/option.rb +64 -0
- data/lib/doorkeeper/config.rb +191 -75
- data/lib/doorkeeper/engine.rb +1 -1
- data/lib/doorkeeper/grape/authorization_decorator.rb +4 -4
- data/lib/doorkeeper/grape/helpers.rb +3 -3
- data/lib/doorkeeper/helpers/controller.rb +3 -2
- data/lib/doorkeeper/models/access_grant_mixin.rb +20 -3
- data/lib/doorkeeper/models/access_token_mixin.rb +63 -19
- data/lib/doorkeeper/models/application_mixin.rb +43 -1
- data/lib/doorkeeper/models/concerns/expirable.rb +4 -2
- data/lib/doorkeeper/models/concerns/ownership.rb +1 -6
- data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
- data/lib/doorkeeper/models/concerns/revocable.rb +2 -1
- data/lib/doorkeeper/models/concerns/scopes.rb +5 -1
- data/lib/doorkeeper/models/concerns/secret_storable.rb +108 -0
- data/lib/doorkeeper/oauth/authorization/code.rb +2 -2
- data/lib/doorkeeper/oauth/authorization/token.rb +12 -7
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +1 -1
- data/lib/doorkeeper/oauth/authorization_code_request.rb +6 -4
- data/lib/doorkeeper/oauth/client.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +5 -4
- data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -1
- data/lib/doorkeeper/oauth/code_response.rb +2 -2
- data/lib/doorkeeper/oauth/error_response.rb +10 -6
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +23 -8
- data/lib/doorkeeper/oauth/helpers/unique_token.rb +13 -1
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +37 -1
- data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -1
- data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
- data/lib/doorkeeper/oauth/pre_authorization.rb +11 -5
- data/lib/doorkeeper/oauth/refresh_token_request.rb +5 -2
- data/lib/doorkeeper/oauth/scopes.rb +5 -3
- data/lib/doorkeeper/oauth/token.rb +2 -2
- data/lib/doorkeeper/oauth/token_introspection.rb +74 -8
- data/lib/doorkeeper/oauth/token_response.rb +9 -9
- data/lib/doorkeeper/oauth.rb +5 -5
- data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -13
- data/lib/doorkeeper/orm/active_record/access_token.rb +6 -13
- data/lib/doorkeeper/orm/active_record/application.rb +21 -57
- data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +10 -3
- data/lib/doorkeeper/orm/active_record.rb +6 -6
- data/lib/doorkeeper/rails/helpers.rb +1 -1
- data/lib/doorkeeper/rails/routes/mapping.rb +7 -7
- data/lib/doorkeeper/rails/routes.rb +11 -11
- data/lib/doorkeeper/rake/db.rake +13 -13
- data/lib/doorkeeper/rake.rb +1 -1
- data/lib/doorkeeper/request.rb +1 -1
- data/lib/doorkeeper/secret_storing/base.rb +64 -0
- data/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
- data/lib/doorkeeper/secret_storing/plain.rb +33 -0
- data/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
- data/lib/doorkeeper/stale_records_cleaner.rb +1 -1
- data/lib/doorkeeper/version.rb +3 -3
- data/lib/doorkeeper.rb +69 -60
- data/lib/generators/doorkeeper/application_owner_generator.rb +10 -9
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +10 -9
- data/lib/generators/doorkeeper/install_generator.rb +11 -9
- data/lib/generators/doorkeeper/migration_generator.rb +9 -9
- data/lib/generators/doorkeeper/pkce_generator.rb +10 -9
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +10 -9
- data/lib/generators/doorkeeper/templates/initializer.rb +120 -13
- data/lib/generators/doorkeeper/templates/migration.rb.erb +15 -7
- data/lib/generators/doorkeeper/views_generator.rb +6 -4
- data/spec/controllers/application_metal_controller_spec.rb +24 -10
- data/spec/controllers/applications_controller_spec.rb +54 -52
- data/spec/controllers/authorizations_controller_spec.rb +139 -145
- data/spec/controllers/protected_resources_controller_spec.rb +78 -76
- data/spec/controllers/token_info_controller_spec.rb +14 -12
- data/spec/controllers/tokens_controller_spec.rb +167 -108
- data/spec/dummy/Rakefile +3 -1
- data/spec/dummy/app/controllers/application_controller.rb +3 -1
- data/spec/dummy/app/controllers/custom_authorizations_controller.rb +2 -0
- data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -2
- data/spec/dummy/app/controllers/home_controller.rb +5 -3
- data/spec/dummy/app/controllers/metal_controller.rb +2 -0
- data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +4 -2
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/user.rb +2 -0
- data/spec/dummy/config/application.rb +25 -1
- data/spec/dummy/config/environments/development.rb +2 -0
- data/spec/dummy/config/environments/production.rb +2 -0
- data/spec/dummy/config/environments/test.rb +3 -1
- data/spec/dummy/config/initializers/backtrace_silencers.rb +2 -0
- data/spec/dummy/config/initializers/doorkeeper.rb +5 -2
- data/spec/dummy/config/initializers/secret_token.rb +3 -1
- data/spec/dummy/config/initializers/session_store.rb +3 -1
- data/spec/dummy/config/initializers/wrap_parameters.rb +2 -0
- data/spec/dummy/config.ru +3 -1
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +17 -10
- data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +2 -0
- data/spec/dummy/db/schema.rb +1 -1
- data/spec/dummy/script/rails +5 -3
- data/spec/factories.rb +8 -6
- data/spec/generators/application_owner_generator_spec.rb +13 -26
- data/spec/generators/confidential_applications_generator_spec.rb +12 -28
- data/spec/generators/install_generator_spec.rb +17 -15
- data/spec/generators/migration_generator_spec.rb +13 -26
- data/spec/generators/pkce_generator_spec.rb +11 -26
- data/spec/generators/previous_refresh_token_generator_spec.rb +16 -29
- data/spec/generators/templates/routes.rb +2 -0
- data/spec/generators/views_generator_spec.rb +14 -12
- data/spec/grape/grape_integration_spec.rb +34 -32
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +9 -7
- data/spec/lib/config_spec.rb +268 -99
- data/spec/lib/doorkeeper_spec.rb +3 -1
- data/spec/lib/models/expirable_spec.rb +22 -8
- data/spec/lib/models/reusable_spec.rb +40 -0
- data/spec/lib/models/revocable_spec.rb +8 -6
- data/spec/lib/models/scopes_spec.rb +27 -13
- data/spec/lib/models/secret_storable_spec.rb +135 -0
- data/spec/lib/oauth/authorization/uri_builder_spec.rb +17 -15
- data/spec/lib/oauth/authorization_code_request_spec.rb +34 -11
- data/spec/lib/oauth/base_request_spec.rb +20 -8
- data/spec/lib/oauth/base_response_spec.rb +3 -1
- data/spec/lib/oauth/client/credentials_spec.rb +24 -22
- data/spec/lib/oauth/client_credentials/creator_spec.rb +56 -10
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +27 -18
- data/spec/lib/oauth/client_credentials/validation_spec.rb +20 -15
- data/spec/lib/oauth/client_credentials_integration_spec.rb +7 -5
- data/spec/lib/oauth/client_credentials_request_spec.rb +27 -21
- data/spec/lib/oauth/client_spec.rb +15 -13
- data/spec/lib/oauth/code_request_spec.rb +8 -6
- data/spec/lib/oauth/code_response_spec.rb +9 -7
- data/spec/lib/oauth/error_response_spec.rb +19 -11
- data/spec/lib/oauth/error_spec.rb +4 -2
- data/spec/lib/oauth/forbidden_token_response_spec.rb +7 -5
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +68 -31
- data/spec/lib/oauth/helpers/unique_token_spec.rb +8 -6
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +115 -95
- data/spec/lib/oauth/invalid_token_response_spec.rb +3 -1
- data/spec/lib/oauth/password_access_token_request_spec.rb +79 -30
- data/spec/lib/oauth/pre_authorization_spec.rb +80 -54
- data/spec/lib/oauth/refresh_token_request_spec.rb +36 -33
- data/spec/lib/oauth/scopes_spec.rb +63 -61
- data/spec/lib/oauth/token_request_spec.rb +79 -24
- data/spec/lib/oauth/token_response_spec.rb +40 -38
- data/spec/lib/oauth/token_spec.rb +60 -44
- data/spec/lib/request/strategy_spec.rb +3 -1
- data/spec/lib/secret_storing/base_spec.rb +60 -0
- data/spec/lib/secret_storing/bcrypt_spec.rb +49 -0
- data/spec/lib/secret_storing/plain_spec.rb +44 -0
- data/spec/lib/secret_storing/sha256_hash_spec.rb +48 -0
- data/spec/lib/server_spec.rb +16 -14
- data/spec/lib/stale_records_cleaner_spec.rb +17 -17
- data/spec/models/doorkeeper/access_grant_spec.rb +80 -15
- data/spec/models/doorkeeper/access_token_spec.rb +198 -73
- data/spec/models/doorkeeper/application_spec.rb +280 -288
- data/spec/requests/applications/applications_request_spec.rb +98 -66
- data/spec/requests/applications/authorized_applications_spec.rb +20 -18
- data/spec/requests/endpoints/authorization_spec.rb +25 -23
- data/spec/requests/endpoints/token_spec.rb +38 -36
- data/spec/requests/flows/authorization_code_errors_spec.rb +26 -24
- data/spec/requests/flows/authorization_code_spec.rb +193 -151
- data/spec/requests/flows/client_credentials_spec.rb +54 -52
- data/spec/requests/flows/implicit_grant_errors_spec.rb +10 -8
- data/spec/requests/flows/implicit_grant_spec.rb +27 -25
- data/spec/requests/flows/password_spec.rb +59 -55
- data/spec/requests/flows/refresh_token_spec.rb +45 -43
- data/spec/requests/flows/revoke_token_spec.rb +40 -54
- data/spec/requests/flows/skip_authorization_spec.rb +23 -21
- data/spec/requests/protected_resources/metal_spec.rb +7 -5
- data/spec/requests/protected_resources/private_api_spec.rb +35 -33
- data/spec/routing/custom_controller_routes_spec.rb +71 -65
- data/spec/routing/default_routes_spec.rb +22 -20
- data/spec/routing/scoped_routes_spec.rb +20 -18
- data/spec/spec_helper.rb +15 -13
- data/spec/spec_helper_integration.rb +3 -1
- data/spec/support/dependencies/factory_bot.rb +3 -1
- data/spec/support/doorkeeper_rspec.rb +3 -1
- data/spec/support/helpers/access_token_request_helper.rb +3 -1
- data/spec/support/helpers/authorization_request_helper.rb +4 -2
- data/spec/support/helpers/config_helper.rb +2 -0
- data/spec/support/helpers/model_helper.rb +3 -1
- data/spec/support/helpers/request_spec_helper.rb +5 -3
- data/spec/support/helpers/url_helper.rb +9 -7
- data/spec/support/http_method_shim.rb +4 -9
- data/spec/support/orm/active_record.rb +3 -1
- data/spec/support/shared/controllers_shared_context.rb +18 -16
- data/spec/support/shared/hashing_shared_context.rb +36 -0
- data/spec/support/shared/models_shared_examples.rb +12 -10
- data/spec/validators/redirect_uri_validator_spec.rb +74 -45
- data/spec/version/version_spec.rb +7 -5
- metadata +60 -26
- data/gemfiles/rails_4_2.gemfile +0 -13
- data/spec/dummy/config/initializers/new_framework_defaults.rb +0 -8
|
@@ -6,23 +6,25 @@ module Doorkeeper
|
|
|
6
6
|
|
|
7
7
|
include OAuth::Helpers
|
|
8
8
|
include Models::Expirable
|
|
9
|
+
include Models::Reusable
|
|
9
10
|
include Models::Revocable
|
|
10
11
|
include Models::Accessible
|
|
11
12
|
include Models::Orderable
|
|
13
|
+
include Models::SecretStorable
|
|
12
14
|
include Models::Scopes
|
|
13
15
|
|
|
14
16
|
module ClassMethods
|
|
15
17
|
# Returns an instance of the Doorkeeper::AccessToken with
|
|
16
|
-
# specific token value.
|
|
18
|
+
# specific plain text token value.
|
|
17
19
|
#
|
|
18
20
|
# @param token [#to_s]
|
|
19
|
-
# token value (any object that responds to `#to_s`)
|
|
21
|
+
# Plain text token value (any object that responds to `#to_s`)
|
|
20
22
|
#
|
|
21
23
|
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
|
|
22
24
|
# if there is no record with such token
|
|
23
25
|
#
|
|
24
26
|
def by_token(token)
|
|
25
|
-
|
|
27
|
+
find_by_plaintext_token(:token, token)
|
|
26
28
|
end
|
|
27
29
|
|
|
28
30
|
# Returns an instance of the Doorkeeper::AccessToken
|
|
@@ -35,7 +37,7 @@ module Doorkeeper
|
|
|
35
37
|
# if there is no record with such refresh token
|
|
36
38
|
#
|
|
37
39
|
def by_refresh_token(refresh_token)
|
|
38
|
-
|
|
40
|
+
find_by_plaintext_token(:refresh_token, refresh_token)
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
# Revokes AccessToken records that have not been revoked and associated
|
|
@@ -98,9 +100,9 @@ module Doorkeeper
|
|
|
98
100
|
|
|
99
101
|
(token_scopes.sort == param_scopes.sort) &&
|
|
100
102
|
Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
|
|
101
|
-
param_scopes.to_s,
|
|
102
|
-
Doorkeeper.configuration.scopes,
|
|
103
|
-
app_scopes
|
|
103
|
+
scope_str: param_scopes.to_s,
|
|
104
|
+
server_scopes: Doorkeeper.configuration.scopes,
|
|
105
|
+
app_scopes: app_scopes
|
|
104
106
|
)
|
|
105
107
|
end
|
|
106
108
|
|
|
@@ -125,14 +127,14 @@ module Doorkeeper
|
|
|
125
127
|
if Doorkeeper.configuration.reuse_access_token
|
|
126
128
|
access_token = matching_token_for(application, resource_owner_id, scopes)
|
|
127
129
|
|
|
128
|
-
return access_token if access_token
|
|
130
|
+
return access_token if access_token&.reusable?
|
|
129
131
|
end
|
|
130
132
|
|
|
131
133
|
create!(
|
|
132
|
-
application_id:
|
|
134
|
+
application_id: application.try(:id),
|
|
133
135
|
resource_owner_id: resource_owner_id,
|
|
134
|
-
scopes:
|
|
135
|
-
expires_in:
|
|
136
|
+
scopes: scopes.to_s,
|
|
137
|
+
expires_in: expires_in,
|
|
136
138
|
use_refresh_token: use_refresh_token
|
|
137
139
|
)
|
|
138
140
|
end
|
|
@@ -168,6 +170,20 @@ module Doorkeeper
|
|
|
168
170
|
def last_authorized_token_for(application_id, resource_owner_id)
|
|
169
171
|
authorized_tokens_for(application_id, resource_owner_id).first
|
|
170
172
|
end
|
|
173
|
+
|
|
174
|
+
##
|
|
175
|
+
# Determines the secret storing transformer
|
|
176
|
+
# Unless configured otherwise, uses the plain secret strategy
|
|
177
|
+
def secret_strategy
|
|
178
|
+
::Doorkeeper.configuration.token_secret_strategy
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
##
|
|
182
|
+
# Determine the fallback storing strategy
|
|
183
|
+
# Unless configured, there will be no fallback
|
|
184
|
+
def fallback_secret_strategy
|
|
185
|
+
::Doorkeeper.configuration.token_secret_fallback_strategy
|
|
186
|
+
end
|
|
171
187
|
end
|
|
172
188
|
|
|
173
189
|
# Access Token type: Bearer.
|
|
@@ -175,7 +191,7 @@ module Doorkeeper
|
|
|
175
191
|
# The OAuth 2.0 Authorization Framework: Bearer Token Usage
|
|
176
192
|
#
|
|
177
193
|
def token_type
|
|
178
|
-
|
|
194
|
+
"Bearer"
|
|
179
195
|
end
|
|
180
196
|
|
|
181
197
|
def use_refresh_token?
|
|
@@ -188,11 +204,11 @@ module Doorkeeper
|
|
|
188
204
|
# @return [Hash] hash with token data
|
|
189
205
|
def as_json(_options = {})
|
|
190
206
|
{
|
|
191
|
-
resource_owner_id:
|
|
192
|
-
scope:
|
|
193
|
-
expires_in:
|
|
194
|
-
application:
|
|
195
|
-
created_at:
|
|
207
|
+
resource_owner_id: resource_owner_id,
|
|
208
|
+
scope: scopes,
|
|
209
|
+
expires_in: expires_in_seconds,
|
|
210
|
+
application: { uid: application.try(:uid) },
|
|
211
|
+
created_at: created_at.to_i,
|
|
196
212
|
}
|
|
197
213
|
end
|
|
198
214
|
|
|
@@ -219,6 +235,30 @@ module Doorkeeper
|
|
|
219
235
|
accessible? && includes_scope?(*scopes)
|
|
220
236
|
end
|
|
221
237
|
|
|
238
|
+
# We keep a volatile copy of the raw refresh token for initial communication
|
|
239
|
+
# The stored refresh_token may be mapped and not available in cleartext.
|
|
240
|
+
def plaintext_refresh_token
|
|
241
|
+
if secret_strategy.allows_restoring_secrets?
|
|
242
|
+
secret_strategy.restore_secret(self, :refresh_token)
|
|
243
|
+
else
|
|
244
|
+
@raw_refresh_token
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# We keep a volatile copy of the raw token for initial communication
|
|
249
|
+
# The stored refresh_token may be mapped and not available in cleartext.
|
|
250
|
+
#
|
|
251
|
+
# Some strategies allow restoring stored secrets (e.g. symmetric encryption)
|
|
252
|
+
# while hashing strategies do not, so you cannot rely on this value
|
|
253
|
+
# returning a present value for persisted tokens.
|
|
254
|
+
def plaintext_token
|
|
255
|
+
if secret_strategy.allows_restoring_secrets?
|
|
256
|
+
secret_strategy.restore_secret(self, :token)
|
|
257
|
+
else
|
|
258
|
+
@raw_token
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
222
262
|
private
|
|
223
263
|
|
|
224
264
|
# Generates refresh token with UniqueToken generator.
|
|
@@ -226,7 +266,8 @@ module Doorkeeper
|
|
|
226
266
|
# @return [String] refresh token value
|
|
227
267
|
#
|
|
228
268
|
def generate_refresh_token
|
|
229
|
-
|
|
269
|
+
@raw_refresh_token = UniqueToken.generate
|
|
270
|
+
secret_strategy.store_secret(self, :refresh_token, @raw_refresh_token)
|
|
230
271
|
end
|
|
231
272
|
|
|
232
273
|
# Generates and sets the token value with the
|
|
@@ -242,13 +283,16 @@ module Doorkeeper
|
|
|
242
283
|
def generate_token
|
|
243
284
|
self.created_at ||= Time.now.utc
|
|
244
285
|
|
|
245
|
-
|
|
286
|
+
@raw_token = token_generator.generate(
|
|
246
287
|
resource_owner_id: resource_owner_id,
|
|
247
288
|
scopes: scopes,
|
|
248
289
|
application: application,
|
|
249
290
|
expires_in: expires_in,
|
|
250
291
|
created_at: created_at
|
|
251
292
|
)
|
|
293
|
+
|
|
294
|
+
secret_strategy.store_secret(self, :token, @raw_token)
|
|
295
|
+
@raw_token
|
|
252
296
|
end
|
|
253
297
|
|
|
254
298
|
def token_generator
|
|
@@ -6,8 +6,10 @@ module Doorkeeper
|
|
|
6
6
|
|
|
7
7
|
include OAuth::Helpers
|
|
8
8
|
include Models::Orderable
|
|
9
|
+
include Models::SecretStorable
|
|
9
10
|
include Models::Scopes
|
|
10
11
|
|
|
12
|
+
# :nodoc
|
|
11
13
|
module ClassMethods
|
|
12
14
|
# Returns an instance of the Doorkeeper::Application with
|
|
13
15
|
# specific UID and secret.
|
|
@@ -25,7 +27,8 @@ module Doorkeeper
|
|
|
25
27
|
app = by_uid(uid)
|
|
26
28
|
return unless app
|
|
27
29
|
return app if secret.blank? && !app.confidential?
|
|
28
|
-
return unless app.secret
|
|
30
|
+
return unless app.secret_matches?(secret)
|
|
31
|
+
|
|
29
32
|
app
|
|
30
33
|
end
|
|
31
34
|
|
|
@@ -39,6 +42,20 @@ module Doorkeeper
|
|
|
39
42
|
def by_uid(uid)
|
|
40
43
|
find_by(uid: uid.to_s)
|
|
41
44
|
end
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
# Determines the secret storing transformer
|
|
48
|
+
# Unless configured otherwise, uses the plain secret strategy
|
|
49
|
+
def secret_strategy
|
|
50
|
+
::Doorkeeper.configuration.application_secret_strategy
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
##
|
|
54
|
+
# Determine the fallback storing strategy
|
|
55
|
+
# Unless configured, there will be no fallback
|
|
56
|
+
def fallback_secret_strategy
|
|
57
|
+
::Doorkeeper.configuration.application_secret_fallback_strategy
|
|
58
|
+
end
|
|
42
59
|
end
|
|
43
60
|
|
|
44
61
|
# Set an application's valid redirect URIs.
|
|
@@ -49,5 +66,30 @@ module Doorkeeper
|
|
|
49
66
|
def redirect_uri=(uris)
|
|
50
67
|
super(uris.is_a?(Array) ? uris.join("\n") : uris)
|
|
51
68
|
end
|
|
69
|
+
|
|
70
|
+
# Check whether the given plain text secret matches our stored secret
|
|
71
|
+
#
|
|
72
|
+
# @param input [#to_s] Plain secret provided by user
|
|
73
|
+
# (any object that responds to `#to_s`)
|
|
74
|
+
#
|
|
75
|
+
# @return [true] Whether the given secret matches the stored secret
|
|
76
|
+
# of this application.
|
|
77
|
+
#
|
|
78
|
+
def secret_matches?(input)
|
|
79
|
+
# return false if either is nil, since secure_compare depends on strings
|
|
80
|
+
# but Application secrets MAY be nil depending on confidentiality.
|
|
81
|
+
return false if input.nil? || secret.nil?
|
|
82
|
+
|
|
83
|
+
# When matching the secret by comparer function, all is well.
|
|
84
|
+
return true if secret_strategy.secret_matches?(input, secret)
|
|
85
|
+
|
|
86
|
+
# When fallback lookup is enabled, ensure applications
|
|
87
|
+
# with plain secrets can still be found
|
|
88
|
+
if fallback_secret_strategy
|
|
89
|
+
fallback_secret_strategy.secret_matches?(input, secret)
|
|
90
|
+
else
|
|
91
|
+
false
|
|
92
|
+
end
|
|
93
|
+
end
|
|
52
94
|
end
|
|
53
95
|
end
|
|
@@ -17,6 +17,7 @@ module Doorkeeper
|
|
|
17
17
|
# or nil if object never expires.
|
|
18
18
|
def expires_in_seconds
|
|
19
19
|
return nil if expires_in.nil?
|
|
20
|
+
|
|
20
21
|
expires = expires_at - Time.now.utc
|
|
21
22
|
expires_sec = expires.seconds.round(0)
|
|
22
23
|
expires_sec > 0 ? expires_sec : 0
|
|
@@ -24,10 +25,11 @@ module Doorkeeper
|
|
|
24
25
|
|
|
25
26
|
# Expiration time (date time of creation + TTL).
|
|
26
27
|
#
|
|
27
|
-
# @return [Time] expiration time in UTC
|
|
28
|
+
# @return [Time, nil] expiration time in UTC
|
|
29
|
+
# or nil if the object never expires.
|
|
28
30
|
#
|
|
29
31
|
def expires_at
|
|
30
|
-
created_at + expires_in.seconds
|
|
32
|
+
expires_in && created_at + expires_in.seconds
|
|
31
33
|
end
|
|
32
34
|
end
|
|
33
35
|
end
|
|
@@ -6,12 +6,7 @@ module Doorkeeper
|
|
|
6
6
|
extend ActiveSupport::Concern
|
|
7
7
|
|
|
8
8
|
included do
|
|
9
|
-
|
|
10
|
-
if defined?(ActiveRecord::Base) && ActiveRecord::VERSION::MAJOR >= 5
|
|
11
|
-
belongs_to_options[:optional] = true
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
belongs_to :owner, belongs_to_options
|
|
9
|
+
belongs_to :owner, polymorphic: true, optional: true
|
|
15
10
|
validates :owner, presence: true, if: :validate_owner?
|
|
16
11
|
end
|
|
17
12
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doorkeeper
|
|
4
|
+
module Models
|
|
5
|
+
module Reusable
|
|
6
|
+
# Indicates whether the object is reusable (i.e. It is not expired and
|
|
7
|
+
# has not crossed reuse_limit).
|
|
8
|
+
#
|
|
9
|
+
# @return [Boolean] true if can be reused and false in other case
|
|
10
|
+
def reusable?
|
|
11
|
+
return false if expired?
|
|
12
|
+
return true unless expires_in
|
|
13
|
+
|
|
14
|
+
threshold_limit = 100 - Doorkeeper.configuration.token_reuse_limit
|
|
15
|
+
expires_in_seconds >= threshold_limit * expires_in / 100
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doorkeeper
|
|
4
|
+
module Models
|
|
5
|
+
##
|
|
6
|
+
# Storable finder to provide lookups for input plaintext values which are
|
|
7
|
+
# mapped to their stored versions (e.g., hashing, encryption) before lookup.
|
|
8
|
+
module SecretStorable
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
delegate :secret_strategy,
|
|
12
|
+
:fallback_secret_strategy,
|
|
13
|
+
to: :class
|
|
14
|
+
|
|
15
|
+
# :nodoc
|
|
16
|
+
module ClassMethods
|
|
17
|
+
# Compare the given plaintext with the secret
|
|
18
|
+
#
|
|
19
|
+
# @param input [String]
|
|
20
|
+
# The plain input to compare.
|
|
21
|
+
#
|
|
22
|
+
# @param secret [String]
|
|
23
|
+
# The secret value to compare with.
|
|
24
|
+
#
|
|
25
|
+
# @return [Boolean]
|
|
26
|
+
# Whether input matches secret as per the secret strategy
|
|
27
|
+
#
|
|
28
|
+
def secret_matches?(input, secret)
|
|
29
|
+
secret_strategy.secret_matches?(input, secret)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Returns an instance of the Doorkeeper::AccessToken with
|
|
33
|
+
# specific token value.
|
|
34
|
+
#
|
|
35
|
+
# @param attr [Symbol]
|
|
36
|
+
# The token attribute we're looking with.
|
|
37
|
+
#
|
|
38
|
+
# @param token [#to_s]
|
|
39
|
+
# token value (any object that responds to `#to_s`)
|
|
40
|
+
#
|
|
41
|
+
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
|
|
42
|
+
# if there is no record with such token
|
|
43
|
+
#
|
|
44
|
+
def find_by_plaintext_token(attr, token)
|
|
45
|
+
token = token.to_s
|
|
46
|
+
|
|
47
|
+
find_by(attr => secret_strategy.transform_secret(token)) ||
|
|
48
|
+
find_by_fallback_token(attr, token)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Allow looking up previously plain tokens as a fallback
|
|
52
|
+
# IFF a fallback strategy has been defined
|
|
53
|
+
#
|
|
54
|
+
# @param attr [Symbol]
|
|
55
|
+
# The token attribute we're looking with.
|
|
56
|
+
#
|
|
57
|
+
# @param plain_secret [#to_s]
|
|
58
|
+
# plain secret value (any object that responds to `#to_s`)
|
|
59
|
+
#
|
|
60
|
+
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
|
|
61
|
+
# if there is no record with such token
|
|
62
|
+
#
|
|
63
|
+
def find_by_fallback_token(attr, plain_secret)
|
|
64
|
+
return nil unless fallback_secret_strategy
|
|
65
|
+
|
|
66
|
+
# Use the previous strategy to look up
|
|
67
|
+
stored_token = fallback_secret_strategy.transform_secret(plain_secret)
|
|
68
|
+
find_by(attr => stored_token).tap do |resource|
|
|
69
|
+
return nil unless resource
|
|
70
|
+
|
|
71
|
+
upgrade_fallback_value resource, attr, plain_secret
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Allow implementations in ORMs to replace a plain
|
|
76
|
+
# value falling back to to avoid it remaining as plain text.
|
|
77
|
+
#
|
|
78
|
+
# @param instance
|
|
79
|
+
# An instance of this model with a plain value token.
|
|
80
|
+
#
|
|
81
|
+
# @param attr
|
|
82
|
+
# The secret attribute name to upgrade.
|
|
83
|
+
#
|
|
84
|
+
# @param plain_secret
|
|
85
|
+
# The plain secret to upgrade.
|
|
86
|
+
#
|
|
87
|
+
def upgrade_fallback_value(instance, attr, plain_secret)
|
|
88
|
+
upgraded = secret_strategy.store_secret(instance, attr, plain_secret)
|
|
89
|
+
instance.update(attr => upgraded)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
##
|
|
93
|
+
# Determines the secret storing transformer
|
|
94
|
+
# Unless configured otherwise, uses the plain secret strategy
|
|
95
|
+
def secret_strategy
|
|
96
|
+
::Doorkeeper::SecretStoring::Plain
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
##
|
|
100
|
+
# Determine the fallback storing strategy
|
|
101
|
+
# Unless configured, there will be no fallback
|
|
102
|
+
def fallback_secret_strategy
|
|
103
|
+
nil
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -16,7 +16,7 @@ module Doorkeeper
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def native_redirect
|
|
19
|
-
{ action: :show, code: token.
|
|
19
|
+
{ action: :show, code: token.plaintext_token }
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def configuration
|
|
@@ -42,7 +42,7 @@ module Doorkeeper
|
|
|
42
42
|
|
|
43
43
|
{
|
|
44
44
|
code_challenge: pre_auth.code_challenge,
|
|
45
|
-
code_challenge_method: pre_auth.code_challenge_method
|
|
45
|
+
code_challenge_method: pre_auth.code_challenge_method,
|
|
46
46
|
}
|
|
47
47
|
end
|
|
48
48
|
|
|
@@ -8,7 +8,9 @@ module Doorkeeper
|
|
|
8
8
|
|
|
9
9
|
class << self
|
|
10
10
|
def build_context(pre_auth_or_oauth_client, grant_type, scopes)
|
|
11
|
-
oauth_client = if pre_auth_or_oauth_client.respond_to?(:
|
|
11
|
+
oauth_client = if pre_auth_or_oauth_client.respond_to?(:application)
|
|
12
|
+
pre_auth_or_oauth_client.application
|
|
13
|
+
elsif pre_auth_or_oauth_client.respond_to?(:client)
|
|
12
14
|
pre_auth_or_oauth_client.client
|
|
13
15
|
else
|
|
14
16
|
pre_auth_or_oauth_client
|
|
@@ -21,11 +23,14 @@ module Doorkeeper
|
|
|
21
23
|
)
|
|
22
24
|
end
|
|
23
25
|
|
|
24
|
-
def access_token_expires_in(
|
|
25
|
-
if (
|
|
26
|
-
expiration
|
|
26
|
+
def access_token_expires_in(configuration, context)
|
|
27
|
+
if configuration.option_defined?(:custom_access_token_expires_in)
|
|
28
|
+
expiration = configuration.custom_access_token_expires_in.call(context)
|
|
29
|
+
return nil if expiration == Float::INFINITY
|
|
30
|
+
|
|
31
|
+
expiration || configuration.access_token_expires_in
|
|
27
32
|
else
|
|
28
|
-
|
|
33
|
+
configuration.access_token_expires_in
|
|
29
34
|
end
|
|
30
35
|
end
|
|
31
36
|
|
|
@@ -62,7 +67,7 @@ module Doorkeeper
|
|
|
62
67
|
{
|
|
63
68
|
controller: controller,
|
|
64
69
|
action: :show,
|
|
65
|
-
access_token: token.
|
|
70
|
+
access_token: token.plaintext_token,
|
|
66
71
|
}
|
|
67
72
|
end
|
|
68
73
|
|
|
@@ -75,7 +80,7 @@ module Doorkeeper
|
|
|
75
80
|
def controller
|
|
76
81
|
@controller ||= begin
|
|
77
82
|
mapping = Doorkeeper::Rails::Routes.mapping[:token_info] || {}
|
|
78
|
-
mapping[:controllers] ||
|
|
83
|
+
mapping[:controllers] || "doorkeeper/token_info"
|
|
79
84
|
end
|
|
80
85
|
end
|
|
81
86
|
end
|
|
@@ -43,17 +43,19 @@ module Doorkeeper
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def validate_attributes
|
|
46
|
-
return false if grant
|
|
46
|
+
return false if grant&.uses_pkce? && code_verifier.blank?
|
|
47
47
|
return false if grant && !grant.pkce_supported? && !code_verifier.blank?
|
|
48
|
+
|
|
48
49
|
redirect_uri.present?
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
def validate_client
|
|
52
|
-
|
|
53
|
+
client.present?
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
def validate_grant
|
|
56
57
|
return false unless grant && grant.application_id == client.id
|
|
58
|
+
|
|
57
59
|
grant.accessible?
|
|
58
60
|
end
|
|
59
61
|
|
|
@@ -70,9 +72,9 @@ module Doorkeeper
|
|
|
70
72
|
return true unless grant.uses_pkce? || code_verifier
|
|
71
73
|
return false unless grant.pkce_supported?
|
|
72
74
|
|
|
73
|
-
if grant.code_challenge_method ==
|
|
75
|
+
if grant.code_challenge_method == "S256"
|
|
74
76
|
grant.code_challenge == AccessGrant.generate_code_challenge(code_verifier)
|
|
75
|
-
elsif grant.code_challenge_method ==
|
|
77
|
+
elsif grant.code_challenge_method == "plain"
|
|
76
78
|
grant.code_challenge == code_verifier
|
|
77
79
|
else
|
|
78
80
|
false
|
|
@@ -18,7 +18,7 @@ module Doorkeeper
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def self.authenticate(credentials, method = Application.method(:by_uid_and_secret))
|
|
21
|
-
return
|
|
21
|
+
return if credentials.blank?
|
|
22
22
|
|
|
23
23
|
if (application = method.call(credentials.uid, credentials.secret))
|
|
24
24
|
new(application)
|
|
@@ -30,13 +30,14 @@ module Doorkeeper
|
|
|
30
30
|
application_scopes = if @client.present?
|
|
31
31
|
@client.application.scopes
|
|
32
32
|
else
|
|
33
|
-
|
|
33
|
+
""
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
ScopeChecker.valid?(
|
|
37
|
-
@request.scopes.to_s,
|
|
38
|
-
@server.scopes,
|
|
39
|
-
application_scopes
|
|
37
|
+
scope_str: @request.scopes.to_s,
|
|
38
|
+
server_scopes: @server.scopes,
|
|
39
|
+
app_scopes: application_scopes,
|
|
40
|
+
grant_type: Doorkeeper::OAuth::CLIENT_CREDENTIALS
|
|
40
41
|
)
|
|
41
42
|
end
|
|
42
43
|
end
|
|
@@ -23,7 +23,7 @@ module Doorkeeper
|
|
|
23
23
|
elsif response_on_fragment
|
|
24
24
|
Authorization::URIBuilder.uri_with_fragment(
|
|
25
25
|
pre_auth.redirect_uri,
|
|
26
|
-
access_token: auth.token.
|
|
26
|
+
access_token: auth.token.plaintext_token,
|
|
27
27
|
token_type: auth.token.token_type,
|
|
28
28
|
expires_in: auth.token.expires_in_seconds,
|
|
29
29
|
state: pre_auth.state
|
|
@@ -31,7 +31,7 @@ module Doorkeeper
|
|
|
31
31
|
else
|
|
32
32
|
Authorization::URIBuilder.uri_with_query(
|
|
33
33
|
pre_auth.redirect_uri,
|
|
34
|
-
code: auth.token.
|
|
34
|
+
code: auth.token.plaintext_token,
|
|
35
35
|
state: pre_auth.state
|
|
36
36
|
)
|
|
37
37
|
end
|
|
@@ -27,12 +27,16 @@ module Doorkeeper
|
|
|
27
27
|
{
|
|
28
28
|
error: name,
|
|
29
29
|
error_description: description,
|
|
30
|
-
state: state
|
|
30
|
+
state: state,
|
|
31
31
|
}.reject { |_, v| v.blank? }
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def status
|
|
35
|
-
:
|
|
35
|
+
if name == :invalid_client
|
|
36
|
+
:unauthorized
|
|
37
|
+
else
|
|
38
|
+
:bad_request
|
|
39
|
+
end
|
|
36
40
|
end
|
|
37
41
|
|
|
38
42
|
def redirectable?
|
|
@@ -50,10 +54,10 @@ module Doorkeeper
|
|
|
50
54
|
|
|
51
55
|
def headers
|
|
52
56
|
{
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
"Cache-Control" => "no-store",
|
|
58
|
+
"Pragma" => "no-cache",
|
|
59
|
+
"Content-Type" => "application/json; charset=utf-8",
|
|
60
|
+
"WWW-Authenticate" => authenticate_info,
|
|
57
61
|
}
|
|
58
62
|
end
|
|
59
63
|
|