doorkeeper 4.3.2 → 5.0.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/.gitignore +1 -0
- data/.gitlab-ci.yml +16 -0
- data/.rubocop.yml +4 -0
- data/.travis.yml +2 -0
- data/Appraisals +2 -2
- data/Gemfile +1 -1
- data/NEWS.md +70 -0
- data/README.md +92 -9
- data/Rakefile +6 -0
- data/UPGRADE.md +2 -0
- data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
- data/app/controllers/doorkeeper/application_controller.rb +4 -3
- data/app/controllers/doorkeeper/application_metal_controller.rb +4 -0
- data/app/controllers/doorkeeper/applications_controller.rb +43 -22
- data/app/controllers/doorkeeper/authorizations_controller.rb +55 -12
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +19 -2
- data/app/controllers/doorkeeper/tokens_controller.rb +11 -16
- data/app/helpers/doorkeeper/dashboard_helper.rb +7 -7
- data/app/validators/redirect_uri_validator.rb +3 -2
- data/app/views/doorkeeper/applications/_delete_form.html.erb +3 -1
- data/app/views/doorkeeper/applications/_form.html.erb +31 -19
- data/app/views/doorkeeper/applications/edit.html.erb +1 -1
- data/app/views/doorkeeper/applications/index.html.erb +18 -6
- data/app/views/doorkeeper/applications/new.html.erb +1 -1
- data/app/views/doorkeeper/applications/show.html.erb +8 -5
- data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
- data/app/views/doorkeeper/authorizations/new.html.erb +4 -0
- data/app/views/layouts/doorkeeper/admin.html.erb +15 -15
- data/config/locales/en.yml +16 -1
- data/doorkeeper.gemspec +18 -18
- data/gemfiles/rails_5_2.gemfile +1 -1
- data/gemfiles/rails_master.gemfile +4 -1
- data/lib/doorkeeper/config.rb +77 -27
- data/lib/doorkeeper/engine.rb +4 -0
- data/lib/doorkeeper/errors.rb +2 -5
- data/lib/doorkeeper/grape/helpers.rb +1 -1
- data/lib/doorkeeper/helpers/controller.rb +7 -2
- data/lib/doorkeeper/models/access_grant_mixin.rb +71 -0
- data/lib/doorkeeper/models/access_token_mixin.rb +40 -23
- data/lib/doorkeeper/models/application_mixin.rb +8 -1
- data/lib/doorkeeper/models/concerns/scopes.rb +1 -1
- data/lib/doorkeeper/oauth/authorization/code.rb +31 -8
- data/lib/doorkeeper/oauth/authorization/context.rb +15 -0
- data/lib/doorkeeper/oauth/authorization/token.rb +36 -14
- data/lib/doorkeeper/oauth/authorization_code_request.rb +27 -2
- data/lib/doorkeeper/oauth/base_request.rb +20 -9
- data/lib/doorkeeper/oauth/client/credentials.rb +4 -2
- data/lib/doorkeeper/oauth/client.rb +0 -2
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +2 -1
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +6 -3
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +4 -6
- data/lib/doorkeeper/oauth/client_credentials_request.rb +0 -4
- data/lib/doorkeeper/oauth/error_response.rb +11 -3
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +0 -8
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +1 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -4
- data/lib/doorkeeper/oauth/pre_authorization.rb +45 -13
- data/lib/doorkeeper/oauth/refresh_token_request.rb +6 -1
- data/lib/doorkeeper/oauth/scopes.rb +1 -1
- data/lib/doorkeeper/oauth/token.rb +5 -2
- data/lib/doorkeeper/oauth/token_introspection.rb +2 -2
- data/lib/doorkeeper/oauth/token_response.rb +4 -2
- data/lib/doorkeeper/oauth.rb +13 -0
- data/lib/doorkeeper/orm/active_record/application.rb +25 -0
- data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +26 -0
- data/lib/doorkeeper/orm/active_record.rb +2 -0
- data/lib/doorkeeper/rails/helpers.rb +2 -4
- data/lib/doorkeeper/rails/routes.rb +13 -1
- data/lib/doorkeeper/rake/db.rake +40 -0
- data/lib/doorkeeper/rake/setup.rake +6 -0
- data/lib/doorkeeper/rake.rb +14 -0
- data/lib/doorkeeper/request/authorization_code.rb +0 -2
- data/lib/doorkeeper/request/client_credentials.rb +0 -2
- data/lib/doorkeeper/request/code.rb +0 -2
- data/lib/doorkeeper/request/password.rb +1 -13
- data/lib/doorkeeper/request/refresh_token.rb +0 -2
- data/lib/doorkeeper/request/token.rb +0 -2
- data/lib/doorkeeper/request.rb +28 -35
- data/lib/doorkeeper/version.rb +5 -4
- data/lib/doorkeeper.rb +19 -17
- data/lib/generators/doorkeeper/application_owner_generator.rb +23 -18
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +32 -0
- data/lib/generators/doorkeeper/install_generator.rb +17 -9
- data/lib/generators/doorkeeper/migration_generator.rb +23 -18
- data/lib/generators/doorkeeper/pkce_generator.rb +32 -0
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +29 -24
- data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
- data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +6 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +76 -11
- data/lib/generators/doorkeeper/templates/migration.rb.erb +1 -0
- data/lib/generators/doorkeeper/views_generator.rb +3 -1
- data/spec/controllers/application_metal_controller_spec.rb +50 -0
- data/spec/controllers/applications_controller_spec.rb +126 -13
- data/spec/controllers/authorizations_controller_spec.rb +279 -17
- data/spec/controllers/protected_resources_controller_spec.rb +16 -16
- data/spec/controllers/token_info_controller_spec.rb +4 -12
- data/spec/controllers/tokens_controller_spec.rb +72 -22
- data/spec/dummy/app/assets/config/manifest.js +2 -0
- data/spec/dummy/config/environments/test.rb +4 -5
- data/spec/dummy/config/initializers/doorkeeper.rb +11 -1
- data/spec/dummy/config/initializers/new_framework_defaults.rb +4 -0
- data/spec/dummy/config/routes.rb +3 -42
- data/spec/dummy/db/migrate/20111122132257_create_users.rb +3 -1
- data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +3 -1
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -1
- data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +3 -1
- data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +3 -1
- data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +6 -0
- data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +13 -0
- data/spec/dummy/db/schema.rb +38 -37
- data/spec/generators/application_owner_generator_spec.rb +1 -1
- data/spec/generators/confidential_applications_generator_spec.rb +45 -0
- data/spec/generators/install_generator_spec.rb +1 -1
- data/spec/generators/migration_generator_spec.rb +1 -1
- data/spec/generators/pkce_generator_spec.rb +43 -0
- data/spec/generators/previous_refresh_token_generator_spec.rb +1 -1
- data/spec/generators/views_generator_spec.rb +1 -1
- data/spec/grape/grape_integration_spec.rb +1 -1
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
- data/spec/lib/config_spec.rb +88 -14
- data/spec/lib/doorkeeper_spec.rb +1 -126
- data/spec/lib/models/expirable_spec.rb +0 -3
- data/spec/lib/models/revocable_spec.rb +0 -2
- data/spec/lib/models/scopes_spec.rb +0 -4
- data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -4
- data/spec/lib/oauth/authorization_code_request_spec.rb +24 -2
- data/spec/lib/oauth/base_request_spec.rb +40 -2
- data/spec/lib/oauth/base_response_spec.rb +1 -1
- data/spec/lib/oauth/client/credentials_spec.rb +5 -5
- data/spec/lib/oauth/client_credentials/creator_spec.rb +5 -1
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +26 -7
- data/spec/lib/oauth/client_credentials/validation_spec.rb +2 -3
- data/spec/lib/oauth/client_credentials_integration_spec.rb +1 -1
- data/spec/lib/oauth/client_credentials_request_spec.rb +3 -5
- data/spec/lib/oauth/client_spec.rb +0 -3
- data/spec/lib/oauth/code_request_spec.rb +4 -2
- data/spec/lib/oauth/error_response_spec.rb +0 -3
- data/spec/lib/oauth/error_spec.rb +0 -2
- data/spec/lib/oauth/forbidden_token_response_spec.rb +1 -4
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -3
- data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -1
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +5 -2
- data/spec/lib/oauth/invalid_token_response_spec.rb +1 -4
- data/spec/lib/oauth/password_access_token_request_spec.rb +37 -2
- data/spec/lib/oauth/pre_authorization_spec.rb +40 -6
- data/spec/lib/oauth/refresh_token_request_spec.rb +11 -7
- data/spec/lib/oauth/scopes_spec.rb +0 -3
- data/spec/lib/oauth/token_request_spec.rb +4 -5
- data/spec/lib/oauth/token_response_spec.rb +0 -1
- data/spec/lib/oauth/token_spec.rb +37 -14
- data/spec/lib/orm/active_record/stale_records_cleaner_spec.rb +79 -0
- data/spec/lib/request/strategy_spec.rb +0 -1
- data/spec/lib/server_spec.rb +1 -1
- data/spec/models/doorkeeper/access_grant_spec.rb +44 -1
- data/spec/models/doorkeeper/access_token_spec.rb +66 -22
- data/spec/models/doorkeeper/application_spec.rb +64 -6
- data/spec/requests/applications/applications_request_spec.rb +134 -1
- data/spec/requests/applications/authorized_applications_spec.rb +1 -1
- data/spec/requests/endpoints/authorization_spec.rb +1 -1
- data/spec/requests/endpoints/token_spec.rb +7 -5
- data/spec/requests/flows/authorization_code_errors_spec.rb +1 -1
- data/spec/requests/flows/authorization_code_spec.rb +198 -2
- data/spec/requests/flows/client_credentials_spec.rb +46 -6
- data/spec/requests/flows/implicit_grant_errors_spec.rb +1 -1
- data/spec/requests/flows/implicit_grant_spec.rb +38 -11
- data/spec/requests/flows/password_spec.rb +120 -23
- data/spec/requests/flows/refresh_token_spec.rb +2 -2
- data/spec/requests/flows/revoke_token_spec.rb +11 -11
- data/spec/requests/flows/skip_authorization_spec.rb +16 -11
- data/spec/requests/protected_resources/metal_spec.rb +1 -1
- data/spec/requests/protected_resources/private_api_spec.rb +1 -1
- data/spec/routing/custom_controller_routes_spec.rb +59 -7
- data/spec/routing/default_routes_spec.rb +2 -2
- data/spec/routing/scoped_routes_spec.rb +16 -2
- data/spec/spec_helper.rb +54 -3
- data/spec/spec_helper_integration.rb +2 -74
- data/spec/support/doorkeeper_rspec.rb +19 -0
- data/spec/support/helpers/authorization_request_helper.rb +4 -4
- data/spec/support/helpers/request_spec_helper.rb +10 -2
- data/spec/support/helpers/url_helper.rb +7 -3
- data/spec/support/http_method_shim.rb +12 -16
- data/spec/validators/redirect_uri_validator_spec.rb +7 -1
- data/spec/version/version_spec.rb +3 -3
- data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
- metadata +36 -10
- data/spec/controllers/application_metal_controller.rb +0 -10
- /data/spec/support/dependencies/{factory_girl.rb → factory_bot.rb} +0 -0
data/lib/doorkeeper/config.rb
CHANGED
|
@@ -15,19 +15,19 @@ module Doorkeeper
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def self.configuration
|
|
18
|
-
@config || (
|
|
18
|
+
@config || (raise MissingConfiguration)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def self.setup_orm_adapter
|
|
22
22
|
@orm_adapter = "doorkeeper/orm/#{configuration.orm}".classify.constantize
|
|
23
|
-
rescue NameError =>
|
|
24
|
-
|
|
25
|
-
[doorkeeper] ORM adapter not found (#{configuration.orm}), or there was an error
|
|
26
|
-
trying to load it.
|
|
27
|
-
|
|
28
|
-
You probably need to add the related gem for this adapter to work with
|
|
29
|
-
doorkeeper.
|
|
30
|
-
|
|
23
|
+
rescue NameError => error
|
|
24
|
+
raise error, "ORM adapter not found (#{configuration.orm})", <<-ERROR_MSG.strip_heredoc
|
|
25
|
+
[doorkeeper] ORM adapter not found (#{configuration.orm}), or there was an error
|
|
26
|
+
trying to load it.
|
|
27
|
+
|
|
28
|
+
You probably need to add the related gem for this adapter to work with
|
|
29
|
+
doorkeeper.
|
|
30
|
+
ERROR_MSG
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def self.setup_orm_models
|
|
@@ -90,7 +90,7 @@ doorkeeper.
|
|
|
90
90
|
#
|
|
91
91
|
# @param methods [Array] Define client credentials
|
|
92
92
|
def client_credentials(*methods)
|
|
93
|
-
@config.instance_variable_set(:@
|
|
93
|
+
@config.instance_variable_set(:@client_credentials_methods, methods)
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
# Change the way access token is authenticated from the request object.
|
|
@@ -103,9 +103,12 @@ doorkeeper.
|
|
|
103
103
|
@config.instance_variable_set(:@access_token_methods, methods)
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
-
# Issue access tokens with refresh token (disabled
|
|
107
|
-
def use_refresh_token
|
|
108
|
-
@config.instance_variable_set(
|
|
106
|
+
# Issue access tokens with refresh token (disabled if not set)
|
|
107
|
+
def use_refresh_token(enabled = true, &block)
|
|
108
|
+
@config.instance_variable_set(
|
|
109
|
+
:@refresh_token_enabled,
|
|
110
|
+
block ? block : enabled
|
|
111
|
+
)
|
|
109
112
|
end
|
|
110
113
|
|
|
111
114
|
# Reuse access token for the same resource owner within an application
|
|
@@ -114,6 +117,25 @@ doorkeeper.
|
|
|
114
117
|
def reuse_access_token
|
|
115
118
|
@config.instance_variable_set(:@reuse_access_token, true)
|
|
116
119
|
end
|
|
120
|
+
|
|
121
|
+
# Use an API mode for applications generated with --api argument
|
|
122
|
+
# It will skip applications controller, disable forgery protection
|
|
123
|
+
def api_only
|
|
124
|
+
@config.instance_variable_set(:@api_only, true)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Forbids creating/updating applications with arbitrary scopes that are
|
|
128
|
+
# not in configuration, i.e. `default_scopes` or `optional_scopes`.
|
|
129
|
+
# (disabled by default)
|
|
130
|
+
def enforce_configured_scopes
|
|
131
|
+
@config.instance_variable_set(:@enforce_configured_scopes, true)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Enforce request content type as the spec requires:
|
|
135
|
+
# disabled by default for backward compatibility.
|
|
136
|
+
def enforce_content_type
|
|
137
|
+
@config.instance_variable_set(:@enforce_content_type, true)
|
|
138
|
+
end
|
|
117
139
|
end
|
|
118
140
|
|
|
119
141
|
module Option
|
|
@@ -179,25 +201,39 @@ doorkeeper.
|
|
|
179
201
|
option :resource_owner_authenticator,
|
|
180
202
|
as: :authenticate_resource_owner,
|
|
181
203
|
default: (lambda do |_routes|
|
|
182
|
-
::Rails.logger.warn(
|
|
204
|
+
::Rails.logger.warn(
|
|
205
|
+
I18n.t('doorkeeper.errors.messages.resource_owner_authenticator_not_configured')
|
|
206
|
+
)
|
|
207
|
+
|
|
183
208
|
nil
|
|
184
209
|
end)
|
|
185
210
|
|
|
186
211
|
option :admin_authenticator,
|
|
187
212
|
as: :authenticate_admin,
|
|
188
|
-
default:
|
|
213
|
+
default: (lambda do |_routes|
|
|
214
|
+
::Rails.logger.warn(
|
|
215
|
+
I18n.t('doorkeeper.errors.messages.admin_authenticator_not_configured')
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
head :forbidden
|
|
219
|
+
end)
|
|
189
220
|
|
|
190
221
|
option :resource_owner_from_credentials,
|
|
191
222
|
default: (lambda do |_routes|
|
|
192
|
-
::Rails.logger.warn(
|
|
223
|
+
::Rails.logger.warn(
|
|
224
|
+
I18n.t('doorkeeper.errors.messages.credential_flow_not_configured')
|
|
225
|
+
)
|
|
226
|
+
|
|
193
227
|
nil
|
|
194
228
|
end)
|
|
229
|
+
option :before_successful_authorization, default: ->(_context) {}
|
|
230
|
+
option :after_successful_authorization, default: ->(_context) {}
|
|
195
231
|
option :before_successful_strategy_response, default: ->(_request) {}
|
|
196
232
|
option :after_successful_strategy_response,
|
|
197
233
|
default: ->(_request, _response) {}
|
|
198
234
|
option :skip_authorization, default: ->(_routes) {}
|
|
199
235
|
option :access_token_expires_in, default: 7200
|
|
200
|
-
option :custom_access_token_expires_in, default: ->(
|
|
236
|
+
option :custom_access_token_expires_in, default: ->(_context) { nil }
|
|
201
237
|
option :authorization_code_expires_in, default: 600
|
|
202
238
|
option :orm, default: :active_record
|
|
203
239
|
option :native_redirect_uri, default: 'urn:ietf:wg:oauth:2.0:oob'
|
|
@@ -232,7 +268,6 @@ doorkeeper.
|
|
|
232
268
|
#
|
|
233
269
|
option :force_ssl_in_redirect_uri, default: !Rails.env.development?
|
|
234
270
|
|
|
235
|
-
|
|
236
271
|
# Use a custom class for generating the access token.
|
|
237
272
|
# https://github.com/doorkeeper-gem/doorkeeper#custom-access-token-generator
|
|
238
273
|
#
|
|
@@ -251,20 +286,35 @@ doorkeeper.
|
|
|
251
286
|
default: 'ActionController::Base'
|
|
252
287
|
|
|
253
288
|
attr_reader :reuse_access_token
|
|
289
|
+
attr_reader :api_only
|
|
290
|
+
attr_reader :enforce_content_type
|
|
291
|
+
|
|
292
|
+
def api_only
|
|
293
|
+
@api_only ||= false
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def enforce_content_type
|
|
297
|
+
@enforce_content_type ||= false
|
|
298
|
+
end
|
|
254
299
|
|
|
255
300
|
def refresh_token_enabled?
|
|
256
|
-
@refresh_token_enabled
|
|
257
|
-
|
|
301
|
+
if defined?(@refresh_token_enabled)
|
|
302
|
+
@refresh_token_enabled
|
|
303
|
+
else
|
|
304
|
+
false
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def enforce_configured_scopes?
|
|
309
|
+
!!(defined?(@enforce_configured_scopes) && @enforce_configured_scopes)
|
|
258
310
|
end
|
|
259
311
|
|
|
260
312
|
def enable_application_owner?
|
|
261
|
-
@enable_application_owner
|
|
262
|
-
!!@enable_application_owner
|
|
313
|
+
!!(defined?(@enable_application_owner) && @enable_application_owner)
|
|
263
314
|
end
|
|
264
315
|
|
|
265
316
|
def confirm_application_owner?
|
|
266
|
-
@confirm_application_owner
|
|
267
|
-
!!@confirm_application_owner
|
|
317
|
+
!!(defined?(@confirm_application_owner) && @confirm_application_owner)
|
|
268
318
|
end
|
|
269
319
|
|
|
270
320
|
def default_scopes
|
|
@@ -280,7 +330,7 @@ doorkeeper.
|
|
|
280
330
|
end
|
|
281
331
|
|
|
282
332
|
def client_credentials_methods
|
|
283
|
-
@
|
|
333
|
+
@client_credentials_methods ||= %i[from_basic from_params]
|
|
284
334
|
end
|
|
285
335
|
|
|
286
336
|
def access_token_methods
|
|
@@ -288,11 +338,11 @@ doorkeeper.
|
|
|
288
338
|
end
|
|
289
339
|
|
|
290
340
|
def authorization_response_types
|
|
291
|
-
@authorization_response_types ||= calculate_authorization_response_types
|
|
341
|
+
@authorization_response_types ||= calculate_authorization_response_types.freeze
|
|
292
342
|
end
|
|
293
343
|
|
|
294
344
|
def token_grant_types
|
|
295
|
-
@token_grant_types ||= calculate_token_grant_types
|
|
345
|
+
@token_grant_types ||= calculate_token_grant_types.freeze
|
|
296
346
|
end
|
|
297
347
|
|
|
298
348
|
private
|
data/lib/doorkeeper/engine.rb
CHANGED
|
@@ -17,6 +17,10 @@ module Doorkeeper
|
|
|
17
17
|
|
|
18
18
|
if defined?(Sprockets) && Sprockets::VERSION.chr.to_i >= 4
|
|
19
19
|
initializer 'doorkeeper.assets.precompile' do |app|
|
|
20
|
+
# Force users to use:
|
|
21
|
+
# //= link doorkeeper/admin/application.css
|
|
22
|
+
# in Doorkeeper 5 for Sprockets 4 instead of precompile.
|
|
23
|
+
# Add note to official docs & Wiki
|
|
20
24
|
app.config.assets.precompile += %w[
|
|
21
25
|
doorkeeper/application.css
|
|
22
26
|
doorkeeper/admin/application.css
|
data/lib/doorkeeper/errors.rb
CHANGED
|
@@ -36,10 +36,7 @@ module Doorkeeper
|
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
class TokenGeneratorNotFound < DoorkeeperError
|
|
43
|
-
end
|
|
39
|
+
UnableToGenerateToken = Class.new(DoorkeeperError)
|
|
40
|
+
TokenGeneratorNotFound = Class.new(DoorkeeperError)
|
|
44
41
|
end
|
|
45
42
|
end
|
|
@@ -30,11 +30,11 @@ module Doorkeeper
|
|
|
30
30
|
|
|
31
31
|
# :doc:
|
|
32
32
|
def doorkeeper_token
|
|
33
|
-
@
|
|
33
|
+
@doorkeeper_token ||= OAuth::Token.authenticate request, *config_methods
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def config_methods
|
|
37
|
-
@
|
|
37
|
+
@config_methods ||= Doorkeeper.configuration.access_token_methods
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def get_error_response_from_exception(exception)
|
|
@@ -51,6 +51,11 @@ module Doorkeeper
|
|
|
51
51
|
def skip_authorization?
|
|
52
52
|
!!instance_exec([@server.current_resource_owner, @pre_auth.client], &Doorkeeper.configuration.skip_authorization)
|
|
53
53
|
end
|
|
54
|
+
|
|
55
|
+
def enforce_content_type
|
|
56
|
+
return if request.content_type == 'application/x-www-form-urlencoded'
|
|
57
|
+
render json: {}, status: :unsupported_media_type
|
|
58
|
+
end
|
|
54
59
|
end
|
|
55
60
|
end
|
|
56
61
|
end
|
|
@@ -9,6 +9,15 @@ module Doorkeeper
|
|
|
9
9
|
include Models::Orderable
|
|
10
10
|
include Models::Scopes
|
|
11
11
|
|
|
12
|
+
# never uses pkce, if pkce migrations were not generated
|
|
13
|
+
def uses_pkce?
|
|
14
|
+
pkce_supported? && code_challenge.present?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def pkce_supported?
|
|
18
|
+
respond_to? :code_challenge
|
|
19
|
+
end
|
|
20
|
+
|
|
12
21
|
module ClassMethods
|
|
13
22
|
# Searches for Doorkeeper::AccessGrant record with the
|
|
14
23
|
# specific token value.
|
|
@@ -21,6 +30,68 @@ module Doorkeeper
|
|
|
21
30
|
def by_token(token)
|
|
22
31
|
find_by(token: token.to_s)
|
|
23
32
|
end
|
|
33
|
+
|
|
34
|
+
# Revokes AccessGrant records that have not been revoked and associated
|
|
35
|
+
# with the specific Application and Resource Owner.
|
|
36
|
+
#
|
|
37
|
+
# @param application_id [Integer]
|
|
38
|
+
# ID of the Application
|
|
39
|
+
# @param resource_owner [ActiveRecord::Base]
|
|
40
|
+
# instance of the Resource Owner model
|
|
41
|
+
#
|
|
42
|
+
def revoke_all_for(application_id, resource_owner, clock = Time)
|
|
43
|
+
where(application_id: application_id,
|
|
44
|
+
resource_owner_id: resource_owner.id,
|
|
45
|
+
revoked_at: nil).
|
|
46
|
+
update_all(revoked_at: clock.now.utc)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Implements PKCE code_challenge encoding without base64 padding as described in the spec.
|
|
50
|
+
# https://tools.ietf.org/html/rfc7636#appendix-A
|
|
51
|
+
# Appendix A. Notes on Implementing Base64url Encoding without Padding
|
|
52
|
+
#
|
|
53
|
+
# This appendix describes how to implement a base64url-encoding
|
|
54
|
+
# function without padding, based upon the standard base64-encoding
|
|
55
|
+
# function that uses padding.
|
|
56
|
+
#
|
|
57
|
+
# To be concrete, example C# code implementing these functions is shown
|
|
58
|
+
# below. Similar code could be used in other languages.
|
|
59
|
+
#
|
|
60
|
+
# static string base64urlencode(byte [] arg)
|
|
61
|
+
# {
|
|
62
|
+
# string s = Convert.ToBase64String(arg); // Regular base64 encoder
|
|
63
|
+
# s = s.Split('=')[0]; // Remove any trailing '='s
|
|
64
|
+
# s = s.Replace('+', '-'); // 62nd char of encoding
|
|
65
|
+
# s = s.Replace('/', '_'); // 63rd char of encoding
|
|
66
|
+
# return s;
|
|
67
|
+
# }
|
|
68
|
+
#
|
|
69
|
+
# An example correspondence between unencoded and encoded values
|
|
70
|
+
# follows. The octet sequence below encodes into the string below,
|
|
71
|
+
# which when decoded, reproduces the octet sequence.
|
|
72
|
+
#
|
|
73
|
+
# 3 236 255 224 193
|
|
74
|
+
#
|
|
75
|
+
# A-z_4ME
|
|
76
|
+
#
|
|
77
|
+
# https://ruby-doc.org/stdlib-2.1.3/libdoc/base64/rdoc/Base64.html#method-i-urlsafe_encode64
|
|
78
|
+
#
|
|
79
|
+
# urlsafe_encode64(bin)
|
|
80
|
+
# Returns the Base64-encoded version of bin. This method complies with
|
|
81
|
+
# “Base 64 Encoding with URL and Filename Safe Alphabet” in RFC 4648.
|
|
82
|
+
# The alphabet uses '-' instead of '+' and '_' instead of '/'.
|
|
83
|
+
|
|
84
|
+
# @param code_verifier [#to_s] a one time use value (any object that responds to `#to_s`)
|
|
85
|
+
#
|
|
86
|
+
# @return [#to_s] An encoded code challenge based on the provided verifier suitable for PKCE validation
|
|
87
|
+
def generate_code_challenge(code_verifier)
|
|
88
|
+
padded_result = Base64.urlsafe_encode64(Digest::SHA256.digest(code_verifier))
|
|
89
|
+
padded_result.split('=')[0] # Remove any trailing '='
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def pkce_supported?
|
|
93
|
+
new.pkce_supported?
|
|
94
|
+
end
|
|
24
95
|
end
|
|
25
96
|
end
|
|
26
97
|
end
|
|
@@ -70,30 +70,34 @@ module Doorkeeper
|
|
|
70
70
|
else
|
|
71
71
|
resource_owner_or_id
|
|
72
72
|
end
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
|
|
74
|
+
tokens = authorized_tokens_for(application.try(:id), resource_owner_id)
|
|
75
|
+
tokens.detect do |token|
|
|
76
|
+
scopes_match?(token.scopes, scopes, application.try(:scopes))
|
|
76
77
|
end
|
|
77
78
|
end
|
|
78
79
|
|
|
79
|
-
# Checks whether the token scopes match the scopes from the parameters
|
|
80
|
-
# Application scopes (if present).
|
|
80
|
+
# Checks whether the token scopes match the scopes from the parameters
|
|
81
81
|
#
|
|
82
82
|
# @param token_scopes [#to_s]
|
|
83
83
|
# set of scopes (any object that responds to `#to_s`)
|
|
84
|
-
# @param param_scopes [
|
|
84
|
+
# @param param_scopes [Doorkeeper::OAuth::Scopes]
|
|
85
85
|
# scopes from params
|
|
86
|
-
# @param app_scopes [
|
|
86
|
+
# @param app_scopes [Doorkeeper::OAuth::Scopes]
|
|
87
87
|
# Application scopes
|
|
88
88
|
#
|
|
89
|
-
# @return [Boolean] true if
|
|
89
|
+
# @return [Boolean] true if the param scopes match the token scopes,
|
|
90
|
+
# and all the param scopes are defined in the application (or in the
|
|
91
|
+
# server configuration if the application doesn't define any scopes),
|
|
90
92
|
# and false in other cases
|
|
91
93
|
#
|
|
92
94
|
def scopes_match?(token_scopes, param_scopes, app_scopes)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
return true if token_scopes.empty? && param_scopes.empty?
|
|
96
|
+
|
|
97
|
+
(token_scopes.sort == param_scopes.sort) &&
|
|
98
|
+
Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
|
|
99
|
+
param_scopes.to_s,
|
|
100
|
+
Doorkeeper.configuration.scopes,
|
|
97
101
|
app_scopes
|
|
98
102
|
)
|
|
99
103
|
end
|
|
@@ -118,9 +122,8 @@ module Doorkeeper
|
|
|
118
122
|
def find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token)
|
|
119
123
|
if Doorkeeper.configuration.reuse_access_token
|
|
120
124
|
access_token = matching_token_for(application, resource_owner_id, scopes)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
end
|
|
125
|
+
|
|
126
|
+
return access_token if access_token && !access_token.expired?
|
|
124
127
|
end
|
|
125
128
|
|
|
126
129
|
create!(
|
|
@@ -132,7 +135,7 @@ module Doorkeeper
|
|
|
132
135
|
)
|
|
133
136
|
end
|
|
134
137
|
|
|
135
|
-
# Looking for not revoked Access Token
|
|
138
|
+
# Looking for not revoked Access Token records that belongs to specific
|
|
136
139
|
# Application and Resource Owner.
|
|
137
140
|
#
|
|
138
141
|
# @param application_id [Integer]
|
|
@@ -140,14 +143,28 @@ module Doorkeeper
|
|
|
140
143
|
# @param resource_owner_id [Integer]
|
|
141
144
|
# ID of the Resource Owner model instance
|
|
142
145
|
#
|
|
146
|
+
# @return [Doorkeeper::AccessToken] array of matching AccessToken objects
|
|
147
|
+
#
|
|
148
|
+
def authorized_tokens_for(application_id, resource_owner_id)
|
|
149
|
+
ordered_by(:created_at, :desc)
|
|
150
|
+
.where(application_id: application_id,
|
|
151
|
+
resource_owner_id: resource_owner_id,
|
|
152
|
+
revoked_at: nil)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Convenience method for backwards-compatibility, return the last
|
|
156
|
+
# matching token for the given Application and Resource Owner.
|
|
157
|
+
#
|
|
158
|
+
# @param application_id [Integer]
|
|
159
|
+
# ID of the Application model instance
|
|
160
|
+
# @param resource_owner_id [Integer]
|
|
161
|
+
# ID of the Resource Owner model instance
|
|
162
|
+
#
|
|
143
163
|
# @return [Doorkeeper::AccessToken, nil] matching AccessToken object or
|
|
144
164
|
# nil if nothing was found
|
|
145
165
|
#
|
|
146
166
|
def last_authorized_token_for(application_id, resource_owner_id)
|
|
147
|
-
|
|
148
|
-
find_by(application_id: application_id,
|
|
149
|
-
resource_owner_id: resource_owner_id,
|
|
150
|
-
revoked_at: nil)
|
|
167
|
+
authorized_tokens_for(application_id, resource_owner_id).first
|
|
151
168
|
end
|
|
152
169
|
end
|
|
153
170
|
|
|
@@ -156,7 +173,7 @@ module Doorkeeper
|
|
|
156
173
|
# The OAuth 2.0 Authorization Framework: Bearer Token Usage
|
|
157
174
|
#
|
|
158
175
|
def token_type
|
|
159
|
-
'
|
|
176
|
+
'Bearer'
|
|
160
177
|
end
|
|
161
178
|
|
|
162
179
|
def use_refresh_token?
|
|
@@ -170,8 +187,8 @@ module Doorkeeper
|
|
|
170
187
|
def as_json(_options = {})
|
|
171
188
|
{
|
|
172
189
|
resource_owner_id: resource_owner_id,
|
|
173
|
-
|
|
174
|
-
|
|
190
|
+
scope: scopes,
|
|
191
|
+
expires_in: expires_in_seconds,
|
|
175
192
|
application: { uid: application.try(:uid) },
|
|
176
193
|
created_at: created_at.to_i
|
|
177
194
|
}
|
|
@@ -10,6 +10,9 @@ module Doorkeeper
|
|
|
10
10
|
# Returns an instance of the Doorkeeper::Application with
|
|
11
11
|
# specific UID and secret.
|
|
12
12
|
#
|
|
13
|
+
# Public/Non-confidential applications will only find by uid if secret is
|
|
14
|
+
# blank.
|
|
15
|
+
#
|
|
13
16
|
# @param uid [#to_s] UID (any object that responds to `#to_s`)
|
|
14
17
|
# @param secret [#to_s] secret (any object that responds to `#to_s`)
|
|
15
18
|
#
|
|
@@ -17,7 +20,11 @@ module Doorkeeper
|
|
|
17
20
|
# if there is no record with such credentials
|
|
18
21
|
#
|
|
19
22
|
def by_uid_and_secret(uid, secret)
|
|
20
|
-
|
|
23
|
+
app = by_uid(uid)
|
|
24
|
+
return unless app
|
|
25
|
+
return app if secret.blank? && !app.confidential?
|
|
26
|
+
return unless app.secret == secret
|
|
27
|
+
app
|
|
21
28
|
end
|
|
22
29
|
|
|
23
30
|
# Returns an instance of the Doorkeeper::Application with specific UID.
|
|
@@ -5,18 +5,12 @@ module Doorkeeper
|
|
|
5
5
|
attr_accessor :pre_auth, :resource_owner, :token
|
|
6
6
|
|
|
7
7
|
def initialize(pre_auth, resource_owner)
|
|
8
|
-
@pre_auth
|
|
8
|
+
@pre_auth = pre_auth
|
|
9
9
|
@resource_owner = resource_owner
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def issue_token
|
|
13
|
-
@token ||= AccessGrant.create!
|
|
14
|
-
application_id: pre_auth.client.id,
|
|
15
|
-
resource_owner_id: resource_owner.id,
|
|
16
|
-
expires_in: configuration.authorization_code_expires_in,
|
|
17
|
-
redirect_uri: pre_auth.redirect_uri,
|
|
18
|
-
scopes: pre_auth.scopes.to_s
|
|
19
|
-
)
|
|
13
|
+
@token ||= AccessGrant.create! access_grant_attributes
|
|
20
14
|
end
|
|
21
15
|
|
|
22
16
|
def native_redirect
|
|
@@ -26,6 +20,35 @@ module Doorkeeper
|
|
|
26
20
|
def configuration
|
|
27
21
|
Doorkeeper.configuration
|
|
28
22
|
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def authorization_code_expires_in
|
|
27
|
+
configuration.authorization_code_expires_in
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def access_grant_attributes
|
|
31
|
+
pkce_attributes.merge application_id: pre_auth.client.id,
|
|
32
|
+
resource_owner_id: resource_owner.id,
|
|
33
|
+
expires_in: authorization_code_expires_in,
|
|
34
|
+
redirect_uri: pre_auth.redirect_uri,
|
|
35
|
+
scopes: pre_auth.scopes.to_s
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def pkce_attributes
|
|
39
|
+
return {} unless pkce_supported?
|
|
40
|
+
|
|
41
|
+
{
|
|
42
|
+
code_challenge: pre_auth.code_challenge,
|
|
43
|
+
code_challenge_method: pre_auth.code_challenge_method
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# ensures firstly, if migration with additional pcke columns was
|
|
48
|
+
# generated and migrated
|
|
49
|
+
def pkce_supported?
|
|
50
|
+
Doorkeeper::AccessGrant.pkce_supported?
|
|
51
|
+
end
|
|
29
52
|
end
|
|
30
53
|
end
|
|
31
54
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Doorkeeper
|
|
2
|
+
module OAuth
|
|
3
|
+
module Authorization
|
|
4
|
+
class Context
|
|
5
|
+
attr_reader :client, :grant_type, :scopes
|
|
6
|
+
|
|
7
|
+
def initialize(client, grant_type, scopes)
|
|
8
|
+
@client = client
|
|
9
|
+
@grant_type = grant_type
|
|
10
|
+
@scopes = scopes
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -5,24 +5,34 @@ module Doorkeeper
|
|
|
5
5
|
attr_accessor :pre_auth, :resource_owner, :token
|
|
6
6
|
|
|
7
7
|
class << self
|
|
8
|
-
def
|
|
9
|
-
if (expiration = custom_expiration(server, pre_auth_or_oauth_client))
|
|
10
|
-
expiration
|
|
11
|
-
else
|
|
12
|
-
server.access_token_expires_in
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
private
|
|
17
|
-
|
|
18
|
-
def custom_expiration(server, pre_auth_or_oauth_client)
|
|
8
|
+
def build_context(pre_auth_or_oauth_client, grant_type, scopes)
|
|
19
9
|
oauth_client = if pre_auth_or_oauth_client.respond_to?(:client)
|
|
20
10
|
pre_auth_or_oauth_client.client
|
|
21
11
|
else
|
|
22
12
|
pre_auth_or_oauth_client
|
|
23
13
|
end
|
|
24
14
|
|
|
25
|
-
|
|
15
|
+
Doorkeeper::OAuth::Authorization::Context.new(
|
|
16
|
+
oauth_client,
|
|
17
|
+
grant_type,
|
|
18
|
+
scopes
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def access_token_expires_in(server, context)
|
|
23
|
+
if (expiration = server.custom_access_token_expires_in.call(context))
|
|
24
|
+
expiration
|
|
25
|
+
else
|
|
26
|
+
server.access_token_expires_in
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def refresh_token_enabled?(server, context)
|
|
31
|
+
if server.refresh_token_enabled?.respond_to? :call
|
|
32
|
+
server.refresh_token_enabled?.call(context)
|
|
33
|
+
else
|
|
34
|
+
!!server.refresh_token_enabled?
|
|
35
|
+
end
|
|
26
36
|
end
|
|
27
37
|
end
|
|
28
38
|
|
|
@@ -32,18 +42,23 @@ module Doorkeeper
|
|
|
32
42
|
end
|
|
33
43
|
|
|
34
44
|
def issue_token
|
|
45
|
+
context = self.class.build_context(
|
|
46
|
+
pre_auth.client,
|
|
47
|
+
Doorkeeper::OAuth::IMPLICIT,
|
|
48
|
+
pre_auth.scopes
|
|
49
|
+
)
|
|
35
50
|
@token ||= AccessToken.find_or_create_for(
|
|
36
51
|
pre_auth.client,
|
|
37
52
|
resource_owner.id,
|
|
38
53
|
pre_auth.scopes,
|
|
39
|
-
self.class.access_token_expires_in(configuration,
|
|
54
|
+
self.class.access_token_expires_in(configuration, context),
|
|
40
55
|
false
|
|
41
56
|
)
|
|
42
57
|
end
|
|
43
58
|
|
|
44
59
|
def native_redirect
|
|
45
60
|
{
|
|
46
|
-
controller:
|
|
61
|
+
controller: controller,
|
|
47
62
|
action: :show,
|
|
48
63
|
access_token: token.token
|
|
49
64
|
}
|
|
@@ -54,6 +69,13 @@ module Doorkeeper
|
|
|
54
69
|
def configuration
|
|
55
70
|
Doorkeeper.configuration
|
|
56
71
|
end
|
|
72
|
+
|
|
73
|
+
def controller
|
|
74
|
+
@controller ||= begin
|
|
75
|
+
mapping = Doorkeeper::Rails::Routes.mapping[:token_info] || {}
|
|
76
|
+
mapping[:controllers] || 'doorkeeper/token_info'
|
|
77
|
+
end
|
|
78
|
+
end
|
|
57
79
|
end
|
|
58
80
|
end
|
|
59
81
|
end
|