doorkeeper 5.0.0 → 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 +5 -5
- data/.hound.yml +2 -1
- data/.rubocop.yml +37 -4
- data/.travis.yml +16 -21
- data/Appraisals +29 -7
- data/Dangerfile +67 -0
- data/Gemfile +19 -5
- data/NEWS.md +107 -10
- data/README.md +75 -470
- data/Rakefile +10 -8
- data/app/controllers/doorkeeper/application_controller.rb +3 -2
- data/app/controllers/doorkeeper/application_metal_controller.rb +4 -13
- data/app/controllers/doorkeeper/applications_controller.rb +21 -7
- data/app/controllers/doorkeeper/authorizations_controller.rb +3 -3
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +2 -0
- data/app/controllers/doorkeeper/token_info_controller.rb +4 -2
- data/app/controllers/doorkeeper/tokens_controller.rb +13 -11
- data/app/helpers/doorkeeper/dashboard_helper.rb +3 -1
- data/app/validators/redirect_uri_validator.rb +7 -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 +28 -24
- 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 +197 -76
- data/lib/doorkeeper/engine.rb +3 -1
- data/lib/doorkeeper/errors.rb +17 -0
- data/lib/doorkeeper/grape/authorization_decorator.rb +6 -4
- data/lib/doorkeeper/grape/helpers.rb +5 -3
- data/lib/doorkeeper/helpers/controller.rb +5 -2
- data/lib/doorkeeper/models/access_grant_mixin.rb +25 -6
- data/lib/doorkeeper/models/access_token_mixin.rb +68 -22
- data/lib/doorkeeper/models/application_mixin.rb +45 -1
- data/lib/doorkeeper/models/concerns/accessible.rb +2 -0
- data/lib/doorkeeper/models/concerns/expirable.rb +6 -2
- data/lib/doorkeeper/models/concerns/orderable.rb +2 -0
- data/lib/doorkeeper/models/concerns/ownership.rb +3 -6
- data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
- data/lib/doorkeeper/models/concerns/revocable.rb +4 -1
- data/lib/doorkeeper/models/concerns/scopes.rb +7 -1
- data/lib/doorkeeper/models/concerns/secret_storable.rb +108 -0
- data/lib/doorkeeper/oauth/authorization/code.rb +4 -2
- data/lib/doorkeeper/oauth/authorization/context.rb +2 -0
- data/lib/doorkeeper/oauth/authorization/token.rb +14 -7
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +3 -1
- data/lib/doorkeeper/oauth/authorization_code_request.rb +8 -4
- data/lib/doorkeeper/oauth/base_request.rb +2 -0
- data/lib/doorkeeper/oauth/base_response.rb +2 -0
- data/lib/doorkeeper/oauth/client/credentials.rb +2 -0
- data/lib/doorkeeper/oauth/client.rb +3 -1
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +2 -0
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +2 -0
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +7 -4
- data/lib/doorkeeper/oauth/client_credentials_request.rb +3 -1
- data/lib/doorkeeper/oauth/code_request.rb +2 -0
- data/lib/doorkeeper/oauth/code_response.rb +4 -2
- data/lib/doorkeeper/oauth/error.rb +2 -0
- data/lib/doorkeeper/oauth/error_response.rb +20 -6
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +10 -3
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +25 -8
- data/lib/doorkeeper/oauth/helpers/unique_token.rb +15 -1
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +42 -3
- data/lib/doorkeeper/oauth/invalid_token_response.rb +22 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -2
- data/lib/doorkeeper/oauth/pre_authorization.rb +13 -5
- data/lib/doorkeeper/oauth/refresh_token_request.rb +15 -4
- data/lib/doorkeeper/oauth/scopes.rb +7 -3
- data/lib/doorkeeper/oauth/token.rb +4 -2
- data/lib/doorkeeper/oauth/token_introspection.rb +76 -8
- data/lib/doorkeeper/oauth/token_request.rb +2 -0
- data/lib/doorkeeper/oauth/token_response.rb +11 -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 +26 -7
- data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +10 -3
- data/lib/doorkeeper/orm/active_record.rb +8 -6
- data/lib/doorkeeper/rails/helpers.rb +5 -1
- data/lib/doorkeeper/rails/routes/mapper.rb +2 -0
- data/lib/doorkeeper/rails/routes/mapping.rb +9 -7
- data/lib/doorkeeper/rails/routes.rb +19 -12
- data/lib/doorkeeper/rake/db.rake +17 -17
- data/lib/doorkeeper/rake.rb +1 -1
- data/lib/doorkeeper/request/authorization_code.rb +2 -0
- data/lib/doorkeeper/request/client_credentials.rb +2 -0
- data/lib/doorkeeper/request/code.rb +2 -0
- data/lib/doorkeeper/request/password.rb +2 -0
- data/lib/doorkeeper/request/refresh_token.rb +2 -0
- data/lib/doorkeeper/request/strategy.rb +2 -0
- data/lib/doorkeeper/request/token.rb +2 -0
- data/lib/doorkeeper/request.rb +3 -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/server.rb +2 -0
- data/lib/doorkeeper/stale_records_cleaner.rb +20 -0
- data/lib/doorkeeper/validations.rb +2 -0
- data/lib/doorkeeper/version.rb +4 -2
- data/lib/doorkeeper.rb +69 -59
- 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 +138 -13
- data/lib/generators/doorkeeper/templates/migration.rb.erb +17 -10
- 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 +76 -78
- data/spec/controllers/authorizations_controller_spec.rb +189 -142
- data/spec/controllers/protected_resources_controller_spec.rb +111 -67
- data/spec/controllers/token_info_controller_spec.rb +14 -12
- data/spec/controllers/tokens_controller_spec.rb +170 -112
- 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 +3 -1
- data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -2
- data/spec/dummy/app/controllers/home_controller.rb +6 -5
- 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 +26 -2
- data/spec/dummy/config/boot.rb +2 -4
- data/spec/dummy/config/environment.rb +1 -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 +4 -2
- data/spec/dummy/config/initializers/backtrace_silencers.rb +2 -0
- data/spec/dummy/config/initializers/doorkeeper.rb +7 -3
- 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 +20 -13
- data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +1 -1
- data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +2 -0
- data/spec/dummy/db/schema.rb +1 -1
- data/spec/dummy/script/rails +7 -4
- data/spec/factories.rb +11 -9
- 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 +19 -14
- 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 -1
- data/spec/generators/views_generator_spec.rb +14 -12
- data/spec/grape/grape_integration_spec.rb +35 -33
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +9 -7
- data/spec/lib/config_spec.rb +290 -104
- data/spec/lib/doorkeeper_spec.rb +8 -6
- 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 +43 -17
- data/spec/lib/oauth/base_request_spec.rb +24 -12
- data/spec/lib/oauth/base_response_spec.rb +3 -1
- data/spec/lib/oauth/client/credentials_spec.rb +26 -24
- data/spec/lib/oauth/client_credentials/creator_spec.rb +56 -10
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +27 -20
- data/spec/lib/oauth/client_credentials/validation_spec.rb +21 -15
- data/spec/lib/oauth/client_credentials_integration_spec.rb +8 -6
- 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 +10 -8
- 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 +124 -93
- data/spec/lib/oauth/invalid_token_response_spec.rb +3 -1
- data/spec/lib/oauth/password_access_token_request_spec.rb +95 -34
- data/spec/lib/oauth/pre_authorization_spec.rb +80 -54
- data/spec/lib/oauth/refresh_token_request_spec.rb +46 -39
- data/spec/lib/oauth/scopes_spec.rb +63 -61
- data/spec/lib/oauth/token_request_spec.rb +80 -25
- data/spec/lib/oauth/token_response_spec.rb +40 -38
- data/spec/lib/oauth/token_spec.rb +64 -45
- 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 +20 -18
- data/spec/lib/{orm/active_record/stale_records_cleaner_spec.rb → stale_records_cleaner_spec.rb} +28 -18
- data/spec/models/doorkeeper/access_grant_spec.rb +80 -15
- data/spec/models/doorkeeper/access_token_spec.rb +212 -83
- data/spec/models/doorkeeper/application_spec.rb +146 -49
- data/spec/requests/applications/applications_request_spec.rb +100 -68
- data/spec/requests/applications/authorized_applications_spec.rb +20 -18
- data/spec/requests/endpoints/authorization_spec.rb +27 -25
- data/spec/requests/endpoints/token_spec.rb +38 -36
- data/spec/requests/flows/authorization_code_errors_spec.rb +27 -25
- data/spec/requests/flows/authorization_code_spec.rb +260 -158
- data/spec/requests/flows/client_credentials_spec.rb +54 -52
- data/spec/requests/flows/implicit_grant_errors_spec.rb +11 -9
- data/spec/requests/flows/implicit_grant_spec.rb +27 -25
- data/spec/requests/flows/password_spec.rb +64 -56
- data/spec/requests/flows/refresh_token_spec.rb +91 -32
- data/spec/requests/flows/revoke_token_spec.rb +49 -63
- 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 +37 -35
- 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 +5 -2
- 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 +11 -5
- data/spec/support/helpers/request_spec_helper.rb +5 -3
- data/spec/support/helpers/url_helper.rb +20 -18
- 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 +68 -10
- 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 +76 -47
- data/spec/version/version_spec.rb +7 -5
- metadata +70 -21
- data/gemfiles/rails_4_2.gemfile +0 -13
- data/spec/dummy/config/initializers/new_framework_defaults.rb +0 -10
|
@@ -1,26 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
module AccessTokenMixin
|
|
3
5
|
extend ActiveSupport::Concern
|
|
4
6
|
|
|
5
7
|
include OAuth::Helpers
|
|
6
8
|
include Models::Expirable
|
|
9
|
+
include Models::Reusable
|
|
7
10
|
include Models::Revocable
|
|
8
11
|
include Models::Accessible
|
|
9
12
|
include Models::Orderable
|
|
13
|
+
include Models::SecretStorable
|
|
10
14
|
include Models::Scopes
|
|
11
15
|
|
|
12
16
|
module ClassMethods
|
|
13
17
|
# Returns an instance of the Doorkeeper::AccessToken with
|
|
14
|
-
# specific token value.
|
|
18
|
+
# specific plain text token value.
|
|
15
19
|
#
|
|
16
20
|
# @param token [#to_s]
|
|
17
|
-
# token value (any object that responds to `#to_s`)
|
|
21
|
+
# Plain text token value (any object that responds to `#to_s`)
|
|
18
22
|
#
|
|
19
23
|
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
|
|
20
24
|
# if there is no record with such token
|
|
21
25
|
#
|
|
22
26
|
def by_token(token)
|
|
23
|
-
|
|
27
|
+
find_by_plaintext_token(:token, token)
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
# Returns an instance of the Doorkeeper::AccessToken
|
|
@@ -33,7 +37,7 @@ module Doorkeeper
|
|
|
33
37
|
# if there is no record with such refresh token
|
|
34
38
|
#
|
|
35
39
|
def by_refresh_token(refresh_token)
|
|
36
|
-
|
|
40
|
+
find_by_plaintext_token(:refresh_token, refresh_token)
|
|
37
41
|
end
|
|
38
42
|
|
|
39
43
|
# Revokes AccessToken records that have not been revoked and associated
|
|
@@ -47,11 +51,11 @@ module Doorkeeper
|
|
|
47
51
|
def revoke_all_for(application_id, resource_owner, clock = Time)
|
|
48
52
|
where(application_id: application_id,
|
|
49
53
|
resource_owner_id: resource_owner.id,
|
|
50
|
-
revoked_at: nil)
|
|
51
|
-
update_all(revoked_at: clock.now.utc)
|
|
54
|
+
revoked_at: nil)
|
|
55
|
+
.update_all(revoked_at: clock.now.utc)
|
|
52
56
|
end
|
|
53
57
|
|
|
54
|
-
# Looking for not
|
|
58
|
+
# Looking for not revoked Access Token with a matching set of scopes
|
|
55
59
|
# that belongs to specific Application and Resource Owner.
|
|
56
60
|
#
|
|
57
61
|
# @param application [Doorkeeper::Application]
|
|
@@ -96,9 +100,9 @@ module Doorkeeper
|
|
|
96
100
|
|
|
97
101
|
(token_scopes.sort == param_scopes.sort) &&
|
|
98
102
|
Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
|
|
99
|
-
param_scopes.to_s,
|
|
100
|
-
Doorkeeper.configuration.scopes,
|
|
101
|
-
app_scopes
|
|
103
|
+
scope_str: param_scopes.to_s,
|
|
104
|
+
server_scopes: Doorkeeper.configuration.scopes,
|
|
105
|
+
app_scopes: app_scopes
|
|
102
106
|
)
|
|
103
107
|
end
|
|
104
108
|
|
|
@@ -123,14 +127,14 @@ module Doorkeeper
|
|
|
123
127
|
if Doorkeeper.configuration.reuse_access_token
|
|
124
128
|
access_token = matching_token_for(application, resource_owner_id, scopes)
|
|
125
129
|
|
|
126
|
-
return access_token if access_token
|
|
130
|
+
return access_token if access_token&.reusable?
|
|
127
131
|
end
|
|
128
132
|
|
|
129
133
|
create!(
|
|
130
|
-
application_id:
|
|
134
|
+
application_id: application.try(:id),
|
|
131
135
|
resource_owner_id: resource_owner_id,
|
|
132
|
-
scopes:
|
|
133
|
-
expires_in:
|
|
136
|
+
scopes: scopes.to_s,
|
|
137
|
+
expires_in: expires_in,
|
|
134
138
|
use_refresh_token: use_refresh_token
|
|
135
139
|
)
|
|
136
140
|
end
|
|
@@ -166,6 +170,20 @@ module Doorkeeper
|
|
|
166
170
|
def last_authorized_token_for(application_id, resource_owner_id)
|
|
167
171
|
authorized_tokens_for(application_id, resource_owner_id).first
|
|
168
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
|
|
169
187
|
end
|
|
170
188
|
|
|
171
189
|
# Access Token type: Bearer.
|
|
@@ -173,7 +191,7 @@ module Doorkeeper
|
|
|
173
191
|
# The OAuth 2.0 Authorization Framework: Bearer Token Usage
|
|
174
192
|
#
|
|
175
193
|
def token_type
|
|
176
|
-
|
|
194
|
+
"Bearer"
|
|
177
195
|
end
|
|
178
196
|
|
|
179
197
|
def use_refresh_token?
|
|
@@ -186,11 +204,11 @@ module Doorkeeper
|
|
|
186
204
|
# @return [Hash] hash with token data
|
|
187
205
|
def as_json(_options = {})
|
|
188
206
|
{
|
|
189
|
-
resource_owner_id:
|
|
190
|
-
scope:
|
|
191
|
-
expires_in:
|
|
192
|
-
application:
|
|
193
|
-
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,
|
|
194
212
|
}
|
|
195
213
|
end
|
|
196
214
|
|
|
@@ -217,6 +235,30 @@ module Doorkeeper
|
|
|
217
235
|
accessible? && includes_scope?(*scopes)
|
|
218
236
|
end
|
|
219
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
|
+
|
|
220
262
|
private
|
|
221
263
|
|
|
222
264
|
# Generates refresh token with UniqueToken generator.
|
|
@@ -224,7 +266,8 @@ module Doorkeeper
|
|
|
224
266
|
# @return [String] refresh token value
|
|
225
267
|
#
|
|
226
268
|
def generate_refresh_token
|
|
227
|
-
|
|
269
|
+
@raw_refresh_token = UniqueToken.generate
|
|
270
|
+
secret_strategy.store_secret(self, :refresh_token, @raw_refresh_token)
|
|
228
271
|
end
|
|
229
272
|
|
|
230
273
|
# Generates and sets the token value with the
|
|
@@ -240,13 +283,16 @@ module Doorkeeper
|
|
|
240
283
|
def generate_token
|
|
241
284
|
self.created_at ||= Time.now.utc
|
|
242
285
|
|
|
243
|
-
|
|
286
|
+
@raw_token = token_generator.generate(
|
|
244
287
|
resource_owner_id: resource_owner_id,
|
|
245
288
|
scopes: scopes,
|
|
246
289
|
application: application,
|
|
247
290
|
expires_in: expires_in,
|
|
248
291
|
created_at: created_at
|
|
249
292
|
)
|
|
293
|
+
|
|
294
|
+
secret_strategy.store_secret(self, :token, @raw_token)
|
|
295
|
+
@raw_token
|
|
250
296
|
end
|
|
251
297
|
|
|
252
298
|
def token_generator
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
module ApplicationMixin
|
|
3
5
|
extend ActiveSupport::Concern
|
|
4
6
|
|
|
5
7
|
include OAuth::Helpers
|
|
6
8
|
include Models::Orderable
|
|
9
|
+
include Models::SecretStorable
|
|
7
10
|
include Models::Scopes
|
|
8
11
|
|
|
12
|
+
# :nodoc
|
|
9
13
|
module ClassMethods
|
|
10
14
|
# Returns an instance of the Doorkeeper::Application with
|
|
11
15
|
# specific UID and secret.
|
|
@@ -23,7 +27,8 @@ module Doorkeeper
|
|
|
23
27
|
app = by_uid(uid)
|
|
24
28
|
return unless app
|
|
25
29
|
return app if secret.blank? && !app.confidential?
|
|
26
|
-
return unless app.secret
|
|
30
|
+
return unless app.secret_matches?(secret)
|
|
31
|
+
|
|
27
32
|
app
|
|
28
33
|
end
|
|
29
34
|
|
|
@@ -37,6 +42,20 @@ module Doorkeeper
|
|
|
37
42
|
def by_uid(uid)
|
|
38
43
|
find_by(uid: uid.to_s)
|
|
39
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
|
|
40
59
|
end
|
|
41
60
|
|
|
42
61
|
# Set an application's valid redirect URIs.
|
|
@@ -47,5 +66,30 @@ module Doorkeeper
|
|
|
47
66
|
def redirect_uri=(uris)
|
|
48
67
|
super(uris.is_a?(Array) ? uris.join("\n") : uris)
|
|
49
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
|
|
50
94
|
end
|
|
51
95
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
module Models
|
|
3
5
|
module Expirable
|
|
@@ -15,6 +17,7 @@ module Doorkeeper
|
|
|
15
17
|
# or nil if object never expires.
|
|
16
18
|
def expires_in_seconds
|
|
17
19
|
return nil if expires_in.nil?
|
|
20
|
+
|
|
18
21
|
expires = expires_at - Time.now.utc
|
|
19
22
|
expires_sec = expires.seconds.round(0)
|
|
20
23
|
expires_sec > 0 ? expires_sec : 0
|
|
@@ -22,10 +25,11 @@ module Doorkeeper
|
|
|
22
25
|
|
|
23
26
|
# Expiration time (date time of creation + TTL).
|
|
24
27
|
#
|
|
25
|
-
# @return [Time] expiration time in UTC
|
|
28
|
+
# @return [Time, nil] expiration time in UTC
|
|
29
|
+
# or nil if the object never expires.
|
|
26
30
|
#
|
|
27
31
|
def expires_at
|
|
28
|
-
created_at + expires_in.seconds
|
|
32
|
+
expires_in && created_at + expires_in.seconds
|
|
29
33
|
end
|
|
30
34
|
end
|
|
31
35
|
end
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
module Models
|
|
3
5
|
module Ownership
|
|
4
6
|
extend ActiveSupport::Concern
|
|
5
7
|
|
|
6
8
|
included do
|
|
7
|
-
|
|
8
|
-
if defined?(ActiveRecord::Base) && ActiveRecord::VERSION::MAJOR >= 5
|
|
9
|
-
belongs_to_options[:optional] = true
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
belongs_to :owner, belongs_to_options
|
|
9
|
+
belongs_to :owner, polymorphic: true, optional: true
|
|
13
10
|
validates :owner, presence: true, if: :validate_owner?
|
|
14
11
|
end
|
|
15
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
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
module Models
|
|
3
5
|
module Revocable
|
|
@@ -23,7 +25,8 @@ module Doorkeeper
|
|
|
23
25
|
#
|
|
24
26
|
def revoke_previous_refresh_token!
|
|
25
27
|
return unless refresh_token_revoked_on_use?
|
|
26
|
-
|
|
28
|
+
|
|
29
|
+
old_refresh_token&.revoke
|
|
27
30
|
update_attribute :previous_refresh_token, ""
|
|
28
31
|
end
|
|
29
32
|
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
module Models
|
|
3
5
|
module Scopes
|
|
4
6
|
def scopes
|
|
5
|
-
OAuth::Scopes.from_string(
|
|
7
|
+
OAuth::Scopes.from_string(scopes_string)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def scopes=(value)
|
|
11
|
+
super Array(value).join(" ")
|
|
6
12
|
end
|
|
7
13
|
|
|
8
14
|
def scopes_string
|
|
@@ -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
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
module OAuth
|
|
3
5
|
module Authorization
|
|
@@ -14,7 +16,7 @@ module Doorkeeper
|
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
def native_redirect
|
|
17
|
-
{ action: :show, code: token.
|
|
19
|
+
{ action: :show, code: token.plaintext_token }
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
def configuration
|
|
@@ -40,7 +42,7 @@ module Doorkeeper
|
|
|
40
42
|
|
|
41
43
|
{
|
|
42
44
|
code_challenge: pre_auth.code_challenge,
|
|
43
|
-
code_challenge_method: pre_auth.code_challenge_method
|
|
45
|
+
code_challenge_method: pre_auth.code_challenge_method,
|
|
44
46
|
}
|
|
45
47
|
end
|
|
46
48
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
module OAuth
|
|
3
5
|
module Authorization
|
|
@@ -6,7 +8,9 @@ module Doorkeeper
|
|
|
6
8
|
|
|
7
9
|
class << self
|
|
8
10
|
def build_context(pre_auth_or_oauth_client, grant_type, scopes)
|
|
9
|
-
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)
|
|
10
14
|
pre_auth_or_oauth_client.client
|
|
11
15
|
else
|
|
12
16
|
pre_auth_or_oauth_client
|
|
@@ -19,11 +23,14 @@ module Doorkeeper
|
|
|
19
23
|
)
|
|
20
24
|
end
|
|
21
25
|
|
|
22
|
-
def access_token_expires_in(
|
|
23
|
-
if (
|
|
24
|
-
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
|
|
25
32
|
else
|
|
26
|
-
|
|
33
|
+
configuration.access_token_expires_in
|
|
27
34
|
end
|
|
28
35
|
end
|
|
29
36
|
|
|
@@ -60,7 +67,7 @@ module Doorkeeper
|
|
|
60
67
|
{
|
|
61
68
|
controller: controller,
|
|
62
69
|
action: :show,
|
|
63
|
-
access_token: token.
|
|
70
|
+
access_token: token.plaintext_token,
|
|
64
71
|
}
|
|
65
72
|
end
|
|
66
73
|
|
|
@@ -73,7 +80,7 @@ module Doorkeeper
|
|
|
73
80
|
def controller
|
|
74
81
|
@controller ||= begin
|
|
75
82
|
mapping = Doorkeeper::Rails::Routes.mapping[:token_info] || {}
|
|
76
|
-
mapping[:controllers] ||
|
|
83
|
+
mapping[:controllers] || "doorkeeper/token_info"
|
|
77
84
|
end
|
|
78
85
|
end
|
|
79
86
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
module OAuth
|
|
3
5
|
class AuthorizationCodeRequest < BaseRequest
|
|
@@ -41,17 +43,19 @@ module Doorkeeper
|
|
|
41
43
|
end
|
|
42
44
|
|
|
43
45
|
def validate_attributes
|
|
44
|
-
return false if grant
|
|
46
|
+
return false if grant&.uses_pkce? && code_verifier.blank?
|
|
45
47
|
return false if grant && !grant.pkce_supported? && !code_verifier.blank?
|
|
48
|
+
|
|
46
49
|
redirect_uri.present?
|
|
47
50
|
end
|
|
48
51
|
|
|
49
52
|
def validate_client
|
|
50
|
-
|
|
53
|
+
client.present?
|
|
51
54
|
end
|
|
52
55
|
|
|
53
56
|
def validate_grant
|
|
54
57
|
return false unless grant && grant.application_id == client.id
|
|
58
|
+
|
|
55
59
|
grant.accessible?
|
|
56
60
|
end
|
|
57
61
|
|
|
@@ -68,9 +72,9 @@ module Doorkeeper
|
|
|
68
72
|
return true unless grant.uses_pkce? || code_verifier
|
|
69
73
|
return false unless grant.pkce_supported?
|
|
70
74
|
|
|
71
|
-
if grant.code_challenge_method ==
|
|
75
|
+
if grant.code_challenge_method == "S256"
|
|
72
76
|
grant.code_challenge == AccessGrant.generate_code_challenge(code_verifier)
|
|
73
|
-
elsif grant.code_challenge_method ==
|
|
77
|
+
elsif grant.code_challenge_method == "plain"
|
|
74
78
|
grant.code_challenge == code_verifier
|
|
75
79
|
else
|
|
76
80
|
false
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
module OAuth
|
|
3
5
|
class Client
|
|
@@ -16,7 +18,7 @@ module Doorkeeper
|
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
def self.authenticate(credentials, method = Application.method(:by_uid_and_secret))
|
|
19
|
-
return
|
|
21
|
+
return if credentials.blank?
|
|
20
22
|
|
|
21
23
|
if (application = method.call(credentials.uid, credentials.secret))
|
|
22
24
|
new(application)
|