doorkeeper 5.9.0 → 5.9.1

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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -1
  3. data/README.md +0 -1
  4. data/app/controllers/doorkeeper/authorizations_controller.rb +5 -1
  5. data/app/controllers/doorkeeper/tokens_controller.rb +7 -9
  6. data/app/views/doorkeeper/applications/show.html.erb +5 -5
  7. data/app/views/doorkeeper/authorizations/new.html.erb +1 -1
  8. data/app/views/doorkeeper/authorizations/show.html.erb +1 -1
  9. data/config/locales/en.yml +11 -11
  10. data/lib/doorkeeper/config/validations.rb +2 -2
  11. data/lib/doorkeeper/config.rb +5 -5
  12. data/lib/doorkeeper/engine.rb +2 -4
  13. data/lib/doorkeeper/errors.rb +2 -2
  14. data/lib/doorkeeper/helpers/controller.rb +5 -3
  15. data/lib/doorkeeper/models/access_grant_mixin.rb +1 -0
  16. data/lib/doorkeeper/models/access_token_mixin.rb +4 -2
  17. data/lib/doorkeeper/models/concerns/expiration_time_sql_math.rb +8 -0
  18. data/lib/doorkeeper/models/concerns/polymorphic_resource_owner.rb +0 -1
  19. data/lib/doorkeeper/oauth/authorization_code_request.rb +1 -0
  20. data/lib/doorkeeper/oauth/client_credentials/creator.rb +4 -3
  21. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +1 -1
  22. data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -0
  23. data/lib/doorkeeper/oauth/error_response.rb +21 -1
  24. data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -7
  25. data/lib/doorkeeper/oauth/refresh_token_request.rb +7 -4
  26. data/lib/doorkeeper/oauth/scopes.rb +3 -2
  27. data/lib/doorkeeper/oauth/token_introspection.rb +7 -3
  28. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +2 -7
  29. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +10 -3
  30. data/lib/doorkeeper/orm/active_record.rb +9 -5
  31. data/lib/doorkeeper/rails/routes.rb +1 -1
  32. data/lib/doorkeeper/version.rb +1 -1
  33. data/lib/doorkeeper.rb +10 -0
  34. metadata +3 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d1b95ea3d64e410d8469ac2ba820cc6df74c03400236a162182eb8b8821ed9b
4
- data.tar.gz: 5b73ae5b6e76bf333adffc34ff49f3bb13d385edda710b7319598dd0a6ce165c
3
+ metadata.gz: dd9d528da47dec74a5bb12a6e9ce672279cebd0a52cc6a714347df6c17828dfd
4
+ data.tar.gz: 61e4dcc2434b3e1c0400e35bf80eef980b3adb81c2a33cbe74e9797fef3963b0
5
5
  SHA512:
6
- metadata.gz: f99c5eec8a04660db855c9f881e32387ea8ed11404f425c0008cc760f4497944ca4c483484070e5371e961c9f4ee7456207a9f09120539709b74c42752ef08c2
7
- data.tar.gz: c644e25d872bda11e3d97dfc3d6acdd1e59f71e27fb0eba626edb3a21fb29837709f52ba777123b1505a952aaf273aacefe8912fec7dcd8b76bdad9b86ae8780
6
+ metadata.gz: 8a3e115278bfb5540ccde8b7ecc4084138935a4a7c928011f38684908c4e5cd0dadbfb14a93bcb4ce39da91a8bcaf599970d39de6d7953752e7c77aa6c03c31e
7
+ data.tar.gz: b62a19e584648c28bd9d4a24e21d3501ac35c85c77de950050c8942bb82be4f821d241b41ebc8544b03c354f93f96e4588c051d1b4eafe77ce6b13c06941ab7c
data/CHANGELOG.md CHANGED
@@ -7,7 +7,34 @@ User-visible changes worth mentioning.
7
7
 
8
8
  ## main
9
9
 
10
- Add your entry here.
10
+ - Add here
11
+
12
+ ## 5.9.1
13
+
14
+ - [#1781] Honor `handle_auth_errors :raise` in `AuthorizationsController#authorize_response`
15
+ - [#1795] Fix: detailed error 'insufficient_scope' in protected resources 403s
16
+ - [#1797] Fix `doorkeeper:db:cleanup` rake task failure on PostgreSQL
17
+ - [#1800] Set `@grant_type` in `ClientCredentialsRequest` and `RefreshTokenRequest` constructors so `request.grant_type` returns
18
+ the correct value in hooks like `before_successful_strategy_response`.
19
+ - [#1802] Fix `filter_parameters` not applied when `Doorkeeper.configure` is called inside to_prepare.
20
+ - [#1804] Use `ActiveSupport.on_load(:active_record)` in ORM hooks to prevent loading ActiveRecord models too early
21
+ - [#1806] Fix token revocation bypass for public clients (RFC 7009)
22
+ - [#1815] Expose `current_resource_owner` as a view helper in `Doorkeeper::ApplicationController`.
23
+ - [#1818] Fix token introspection returning `exp: 0` for non-expiring tokens.
24
+ - [#1784] Remove hardcoded colons from view templates, move punctuation to i18n translation strings.
25
+
26
+ **[IMPORTANT]**: if you have customized Doorkeeper views (`authorizations/new`, `authorizations/show`,
27
+ `applications/show`) or overridden the default `en.yml` translations, you may need to update them.
28
+ Colons are no longer hardcoded in the views — they are now part of the translation strings.
29
+ Update the [doorkeeper-i18n](https://github.com/doorkeeper-gem/doorkeeper-i18n) gem to get the
30
+ updated translations for all locales.
31
+ - [#1820] Remove dead wildcard presence check in `Scopes#dynamic_scope_match?` (internal cleanup, no behavior change).
32
+ - [#1822] Update Rubocop config, auto-corrections.
33
+ - [#1823] Update Rubocop config, part 2.
34
+ - [#1825] Update Rubocop config, part 3.
35
+ - [#1821] Fix noisy `Could not find command "no_previous_refresh_token_column?"` Thor output during the
36
+ `PreviousRefreshTokenGenerator` spec by stubbing the underlying DB column check instead of the generator's
37
+ private method (test-only change).
11
38
 
12
39
  ## 5.9.0
13
40
 
data/README.md CHANGED
@@ -4,7 +4,6 @@
4
4
  [![CI](https://github.com/doorkeeper-gem/doorkeeper/actions/workflows/ci.yml/badge.svg)](https://github.com/doorkeeper-gem/doorkeeper/actions/workflows/ci.yml)
5
5
  [![Maintainability](https://qlty.sh/gh/doorkeeper-gem/projects/doorkeeper/maintainability.svg)](https://qlty.sh/gh/doorkeeper-gem/projects/doorkeeper)
6
6
  [![Coverage Status](https://coveralls.io/repos/github/doorkeeper-gem/doorkeeper/badge.svg?branch=main)](https://coveralls.io/github/doorkeeper-gem/doorkeeper?branch=main)
7
- [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com)
8
7
  [![GuardRails badge](https://api.guardrails.io/v2/badges/21183?token=66768ce8f6995814df81f65a2cff40f739f688492704f973e62809e15599bb62)](https://dashboard.guardrails.io/gh/doorkeeper-gem/repos/21183)
9
8
  [![Dependabot](https://img.shields.io/badge/dependabot-enabled-success.svg)](https://dependabot.com)
10
9
 
@@ -131,7 +131,11 @@ module Doorkeeper
131
131
 
132
132
  def authorize_response
133
133
  @authorize_response ||= begin
134
- return pre_auth.error_response unless pre_auth.authorizable?
134
+ unless pre_auth.authorizable?
135
+ response = pre_auth.error_response
136
+ response.raise_exception! if Doorkeeper.config.raise_on_errors?
137
+ return response
138
+ end
135
139
 
136
140
  context = build_context(pre_auth: pre_auth)
137
141
  before_successful_authorization(context)
@@ -87,24 +87,22 @@ module Doorkeeper
87
87
  # credentials, in the case of a confidential client. The token being
88
88
  # revoked must also belong to the requesting client.
89
89
  #
90
- # Once a confidential client is authenticated, it must be authorized to
90
+ # Once a client is authenticated, it must be authorized to
91
91
  # revoke the provided access or refresh token. This ensures one client
92
92
  # cannot revoke another's tokens.
93
93
  #
94
- # Doorkeeper determines the client type implicitly via the presence of the
95
- # OAuth client associated with a given access or refresh token. Since public
96
- # clients authenticate the resource owner via "password" or "implicit" grant
97
- # types, they set the application_id as null (since the claim cannot be
98
- # verified).
94
+ # Doorkeeper checks token ownership for any token that has an
95
+ # application_id set. Tokens issued without a client (application_id
96
+ # is null) can be revoked without client authorization.
99
97
  #
100
98
  # https://datatracker.ietf.org/doc/html/rfc6749#section-2.1
101
99
  # https://datatracker.ietf.org/doc/html/rfc7009
102
100
  def authorized?
103
101
  # Token belongs to specific client, so we need to check if
104
102
  # authenticated client could access it.
105
- if token.application_id? && token.application.confidential?
106
- # We authorize client by checking token's application
107
- server.client && server.client.application == token.application
103
+ if token.application_id?
104
+ # We authorize client by comparing client and token application IDs
105
+ server.client && server.client.id == token.application_id
108
106
  else
109
107
  # Token was issued without client, authorization unnecessary
110
108
  true
@@ -4,10 +4,10 @@
4
4
 
5
5
  <div class="row">
6
6
  <div class="col-md-8">
7
- <h4><%= t('.application_id') %>:</h4>
7
+ <h4><%= t('.application_id') %></h4>
8
8
  <p><code class="bg-light" id="application_id"><%= @application.uid %></code></p>
9
9
 
10
- <h4><%= t('.secret') %>:</h4>
10
+ <h4><%= t('.secret') %></h4>
11
11
  <p>
12
12
  <code class="bg-light" id="secret">
13
13
  <% secret = flash[:application_secret].presence || @application.plaintext_secret %>
@@ -19,7 +19,7 @@
19
19
  </code>
20
20
  </p>
21
21
 
22
- <h4><%= t('.scopes') %>:</h4>
22
+ <h4><%= t('.scopes') %></h4>
23
23
  <p>
24
24
  <code class="bg-light" id="scopes">
25
25
  <% if @application.scopes.present? %>
@@ -30,10 +30,10 @@
30
30
  </code>
31
31
  </p>
32
32
 
33
- <h4><%= t('.confidential') %>:</h4>
33
+ <h4><%= t('.confidential') %></h4>
34
34
  <p><code class="bg-light" id="confidential"><%= @application.confidential? %></code></p>
35
35
 
36
- <h4><%= t('.callback_urls') %>:</h4>
36
+ <h4><%= t('.callback_urls') %></h4>
37
37
 
38
38
  <% if @application.redirect_uri.present? %>
39
39
  <table>
@@ -9,7 +9,7 @@
9
9
 
10
10
  <% if @pre_auth.scopes.count > 0 %>
11
11
  <div id="oauth-permissions">
12
- <p><%= t('.able_to') %>:</p>
12
+ <p><%= t('.able_to') %></p>
13
13
 
14
14
  <ul class="text-info">
15
15
  <% @pre_auth.scopes.each do |scope| %>
@@ -1,5 +1,5 @@
1
1
  <header class="page-header">
2
- <h1><%= t('.title') %>:</h1>
2
+ <h1><%= t('.title') %></h1>
3
3
  </header>
4
4
 
5
5
  <main role="main">
@@ -16,7 +16,7 @@ en:
16
16
  secured_uri: 'must be an HTTPS/SSL URI.'
17
17
  forbidden_uri: 'is forbidden by the server.'
18
18
  scopes:
19
- not_match_configured: "doesn't match configured on the server."
19
+ not_match_configured: "doesn't match those configured on the server."
20
20
 
21
21
  doorkeeper:
22
22
  applications:
@@ -33,7 +33,7 @@ en:
33
33
  help:
34
34
  confidential: 'Application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential.'
35
35
  redirect_uri: 'Use one line per URI'
36
- blank_redirect_uri: "Leave it blank if you configured your provider to use Client Credentials, Resource Owner Password Credentials or any other grant type that doesn't require redirect URI."
36
+ blank_redirect_uri: "Leave it blank if you configured your provider to use Client Credentials, Resource Owner Password Credentials or any other grant type that doesn't require a redirect URI."
37
37
  scopes: 'Separate scopes with spaces. Leave blank to use the default scopes.'
38
38
  edit:
39
39
  title: 'Edit application'
@@ -51,12 +51,12 @@ en:
51
51
  title: 'New Application'
52
52
  show:
53
53
  title: 'Application: %{name}'
54
- application_id: 'UID'
55
- secret: 'Secret'
54
+ application_id: 'UID:'
55
+ secret: 'Secret:'
56
56
  secret_hashed: 'Secret hashed'
57
- scopes: 'Scopes'
58
- confidential: 'Confidential'
59
- callback_urls: 'Callback urls'
57
+ scopes: 'Scopes:'
58
+ confidential: 'Confidential:'
59
+ callback_urls: 'Callback URLs:'
60
60
  actions: 'Actions'
61
61
  not_defined: 'Not defined'
62
62
 
@@ -69,9 +69,9 @@ en:
69
69
  new:
70
70
  title: 'Authorization required'
71
71
  prompt: 'Authorize %{client_name} to use your account?'
72
- able_to: 'This application will be able to'
72
+ able_to: 'This application will be able to:'
73
73
  show:
74
- title: 'Authorization code'
74
+ title: 'Authorization code:'
75
75
  form_post:
76
76
  title: 'Submit this form'
77
77
 
@@ -95,9 +95,9 @@ en:
95
95
  invalid_request:
96
96
  unknown: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.'
97
97
  missing_param: 'Missing required parameter: %{value}.'
98
- request_not_authorized: 'Request need to be authorized. Required parameter for authorizing request is missing or invalid.'
98
+ request_not_authorized: 'Request needs to be authorized. Required parameter for authorizing the request is missing or invalid.'
99
99
  invalid_code_challenge: 'Code challenge is required.'
100
- invalid_redirect_uri: "The requested redirect uri is malformed or doesn't match client redirect URI."
100
+ invalid_redirect_uri: "The requested redirect URI is malformed or doesn't match the client redirect URI."
101
101
  unauthorized_client: 'The client is not authorized to perform this request using this method.'
102
102
  access_denied: 'The resource owner or authorization server denied the request.'
103
103
  invalid_scope: 'The requested scope is invalid, unknown, or malformed.'
@@ -51,14 +51,14 @@ module Doorkeeper
51
51
  end
52
52
 
53
53
  def validate_pkce_code_challenge_methods
54
- return if pkce_code_challenge_methods.all? {|method| method =~ /^plain$|^S256$/ }
54
+ return if pkce_code_challenge_methods.all? { |method| method =~ /^plain$|^S256$/ }
55
55
 
56
56
  ::Rails.logger.warn(
57
57
  "[DOORKEEPER] You have configured an invalid value for pkce_code_challenge_methods option. " \
58
58
  "It will be set to default ['plain', 'S256']",
59
59
  )
60
60
 
61
- @pkce_code_challenge_methods = ['plain', 'S256']
61
+ @pkce_code_challenge_methods = ["plain", "S256"]
62
62
  end
63
63
  end
64
64
  end
@@ -38,7 +38,7 @@ module Doorkeeper
38
38
  # @param opts [Hash] the options to configure dynamic scopes
39
39
  def enable_dynamic_scopes(opts = {})
40
40
  @config.instance_variable_set(:@enable_dynamic_scopes, true)
41
- @config.instance_variable_set(:@dynamic_scopes_delimiter, opts[:delimiter] || ':')
41
+ @config.instance_variable_set(:@dynamic_scopes_delimiter, opts[:delimiter] || ":")
42
42
  end
43
43
 
44
44
  # Define default access token scopes for your provider
@@ -115,7 +115,7 @@ module Doorkeeper
115
115
  @config.instance_variable_set(:@enable_multiple_database_roles, true)
116
116
  end
117
117
 
118
- # Choose to use the url path for native autorization codes
118
+ # Choose to use the url path for native autorization codes
119
119
  # Enabling this flag sets the authorization code response route for
120
120
  # native redirect uris to oauth/authorize/<code>. The default is
121
121
  # oauth/authorize/native?code=<code>.
@@ -592,7 +592,7 @@ module Doorkeeper
592
592
 
593
593
  def pkce_code_challenge_methods_supported
594
594
  return [] unless access_grant_model.pkce_supported?
595
-
595
+
596
596
  pkce_code_challenge_methods
597
597
  end
598
598
 
@@ -633,10 +633,10 @@ module Doorkeeper
633
633
  def deprecated_token_grant_types_resolver
634
634
  @deprecated_token_grant_types ||= calculate_token_grant_types
635
635
  end
636
-
636
+
637
637
  def native_authorization_code_route
638
638
  @use_url_path_for_native_authorization = false unless defined?(@use_url_path_for_native_authorization)
639
- @use_url_path_for_native_authorization ? '/:code' : '/native'
639
+ @use_url_path_for_native_authorization ? "/:code" : "/native"
640
640
  end
641
641
 
642
642
  # [NOTE]: deprecated and will be removed soon
@@ -3,10 +3,8 @@
3
3
  module Doorkeeper
4
4
  class Engine < Rails::Engine
5
5
  initializer "doorkeeper.params.filter", after: :load_config_initializers do |app|
6
- if Doorkeeper.configured?
7
- parameters = %w[client_secret authentication_token access_token refresh_token]
8
- parameters << "code" if Doorkeeper.config.grant_flows.include?("authorization_code")
9
- app.config.filter_parameters << /^(#{Regexp.union(parameters)})$/
6
+ app.config.to_prepare do
7
+ Doorkeeper.setup_filter_parameters
10
8
  end
11
9
  end
12
10
 
@@ -45,7 +45,7 @@ module Doorkeeper
45
45
  end
46
46
 
47
47
  def self.name_for_response
48
- self.name.demodulize.underscore.to_sym
48
+ name.demodulize.underscore.to_sym
49
49
  end
50
50
  end
51
51
 
@@ -54,7 +54,7 @@ module Doorkeeper
54
54
  challenge_methods = Doorkeeper.config.pkce_code_challenge_methods_supported
55
55
  {
56
56
  challenge_methods: challenge_methods.join(", "),
57
- count: challenge_methods.length
57
+ count: challenge_methods.length,
58
58
  }
59
59
  end
60
60
  end
@@ -7,6 +7,10 @@ module Doorkeeper
7
7
  # Rails controller helpers.
8
8
  #
9
9
  module Controller
10
+ def self.included(base)
11
+ base.helper_method :current_resource_owner if base.respond_to?(:helper_method)
12
+ end
13
+
10
14
  private
11
15
 
12
16
  # :doc:
@@ -18,9 +22,7 @@ module Doorkeeper
18
22
  def current_resource_owner
19
23
  return @current_resource_owner if defined?(@current_resource_owner)
20
24
 
21
- @current_resource_owner ||= begin
22
- instance_eval(&Doorkeeper.config.authenticate_resource_owner)
23
- end
25
+ @current_resource_owner ||= instance_eval(&Doorkeeper.config.authenticate_resource_owner)
24
26
  end
25
27
 
26
28
  def resource_owner_from_credentials
@@ -13,6 +13,7 @@ module Doorkeeper
13
13
  include Models::Scopes
14
14
  include Models::ResourceOwnerable
15
15
  include Models::Concerns::WriteToPrimary
16
+ include Models::ExpirationTimeSqlMath
16
17
 
17
18
  # Never uses PKCE if PKCE migrations were not generated
18
19
  def uses_pkce?
@@ -222,7 +222,8 @@ module Doorkeeper
222
222
  if Doorkeeper.config.reuse_access_token
223
223
  custom_attributes = extract_custom_attributes(token_attributes).presence
224
224
  access_token = matching_token_for(
225
- application, resource_owner, scopes, custom_attributes: custom_attributes, include_expired: false)
225
+ application, resource_owner, scopes, custom_attributes: custom_attributes, include_expired: false,
226
+ )
226
227
 
227
228
  return access_token if access_token&.reusable?
228
229
  end
@@ -329,7 +330,8 @@ module Doorkeeper
329
330
  # A hash containing only the custom access token attributes.
330
331
  def extract_custom_attributes(attributes)
331
332
  attributes.with_indifferent_access.slice(
332
- *Doorkeeper.configuration.custom_access_token_attributes)
333
+ *Doorkeeper.configuration.custom_access_token_attributes,
334
+ )
333
335
  end
334
336
  end
335
337
 
@@ -5,6 +5,14 @@ module Doorkeeper
5
5
  module ExpirationTimeSqlMath
6
6
  extend ::ActiveSupport::Concern
7
7
 
8
+ WARNING_MESSAGE = <<~WARNING.squish
9
+ [DOORKEEPER] Doorkeeper doesn't support expiration time math for your database adapter.
10
+ Records with an individual expires_in value longer than the global TTL may be incorrectly processed.
11
+ Please add a class method `custom_expiration_time_sql` to your AccessToken/AccessGrant models/mixins to provide a custom
12
+ SQL expression to calculate access token expiration time. See lib/doorkeeper/orm/active_record/mixins/access_token.rb
13
+ for more details.
14
+ WARNING
15
+
8
16
  class ExpirationTimeSqlGenerator
9
17
  attr_reader :model
10
18
 
@@ -27,4 +27,3 @@ module Doorkeeper
27
27
  end
28
28
  end
29
29
  end
30
-
@@ -14,6 +14,7 @@ module Doorkeeper
14
14
  :invalid_request_reason, :missing_param
15
15
 
16
16
  def initialize(server, grant, client, parameters = {})
17
+ super()
17
18
  @server = server
18
19
  @client = client
19
20
  @grant = grant
@@ -45,10 +45,11 @@ module Doorkeeper
45
45
  end
46
46
 
47
47
  def find_active_existing_token_for(client, scopes, attributes)
48
- custom_attributes = Doorkeeper.config.access_token_model.
49
- extract_custom_attributes(attributes).presence
48
+ custom_attributes = Doorkeeper.config.access_token_model
49
+ .extract_custom_attributes(attributes).presence
50
50
  Doorkeeper.config.access_token_model.matching_token_for(
51
- client, nil, scopes, custom_attributes: custom_attributes, include_expired: false)
51
+ client, nil, scopes, custom_attributes: custom_attributes, include_expired: false,
52
+ )
52
53
  end
53
54
  end
54
55
  end
@@ -39,7 +39,7 @@ module Doorkeeper
39
39
  scopes,
40
40
  use_refresh_token: false,
41
41
  expires_in: ttl,
42
- **attributes
42
+ **attributes,
43
43
  )
44
44
  end
45
45
  end
@@ -13,6 +13,7 @@ module Doorkeeper
13
13
  @client = client
14
14
  @server = server
15
15
  @response = nil
16
+ @grant_type = Doorkeeper::OAuth::CLIENT_CREDENTIALS
16
17
  @original_scopes = parameters[:scope]
17
18
  @parameters = parameters.except(:scope)
18
19
  end
@@ -88,13 +88,33 @@ module Doorkeeper
88
88
 
89
89
  def exception_class
90
90
  return @exception_class if @exception_class
91
+
91
92
  raise NotImplementedError, "error response must define #exception_class"
92
93
  end
93
94
 
94
95
  private
95
96
 
96
97
  def authenticate_info
97
- %(Bearer realm="#{realm}", error="#{name}", error_description="#{description}")
98
+ %(Bearer realm="#{realm}", error="#{sanitize_error_values(name)}", error_description="#{sanitize_error_values(description)}")
99
+ end
100
+
101
+ # This method removes any characters that are invalid in error
102
+ # details per RFC6750.
103
+ #
104
+ # > Values for the "error" and "error_description" attributes
105
+ # > (specified in Appendixes A.7 and A.8 of [RFC6749]) MUST NOT
106
+ # > include characters outside the set %x20-21 (" " or "!") / %x23-5B /
107
+ # > %x5D-7E (ascii "#" to "~" without "\").
108
+ def sanitize_error_values(string)
109
+ string.to_s.each_char.map do |char|
110
+ if char.in?("\x20".encode("utf-8").."\x21".encode("utf-8")) ||
111
+ char.in?("\x23".encode("utf-8").."\x5B".encode("utf-8")) ||
112
+ char.in?("\x5D".encode("utf-8").."\x7E".encode("utf-8"))
113
+ char
114
+ else
115
+ "_"
116
+ end
117
+ end.join("")
98
118
  end
99
119
  end
100
120
  end
@@ -8,7 +8,7 @@ module Doorkeeper
8
8
  end
9
9
 
10
10
  def initialize(attributes = {})
11
- super(attributes.merge(name: :invalid_scope, state: :forbidden))
11
+ super(attributes.merge(name: :insufficient_scope, state: :forbidden))
12
12
  @scopes = attributes[:scopes]
13
13
  end
14
14
 
@@ -16,12 +16,6 @@ module Doorkeeper
16
16
  :forbidden
17
17
  end
18
18
 
19
- def headers
20
- headers = super
21
- headers.delete "WWW-Authenticate"
22
- headers
23
- end
24
-
25
19
  def description
26
20
  @description ||= I18n.t("doorkeeper.errors.messages.forbidden_token.missing_scope",
27
21
  oauth_scopes: @scopes.map(&:to_s).join(" "),)
@@ -18,6 +18,7 @@ module Doorkeeper
18
18
  @server = server
19
19
  @refresh_token = refresh_token
20
20
  @credentials = credentials
21
+ @grant_type = Doorkeeper::OAuth::REFRESH_TOKEN
21
22
  @original_scopes = parameters[:scope] || parameters[:scopes]
22
23
  @refresh_token_parameter = parameters[:refresh_token]
23
24
  @client = load_client(credentials) if credentials
@@ -36,12 +37,14 @@ module Doorkeeper
36
37
  # This allows multiple concurrent refresh requests to succeed during the
37
38
  # transition period, after which the old refresh token will be revoked.
38
39
  raise Errors::InvalidGrantReuse if refresh_token.revoked?
40
+
39
41
  create_access_token
40
42
  else
41
43
  # Use locking when refresh tokens are revoked immediately
42
44
  # to prevent race conditions where multiple tokens could be created
43
45
  refresh_token.with_lock do
44
46
  raise Errors::InvalidGrantReuse if refresh_token.revoked?
47
+
45
48
  refresh_token.revoke
46
49
  create_access_token
47
50
  end
@@ -131,10 +134,10 @@ module Doorkeeper
131
134
 
132
135
  def custom_token_attributes_with_data
133
136
  refresh_token
134
- .attributes
135
- .with_indifferent_access
136
- .slice(*Doorkeeper.config.custom_access_token_attributes)
137
- .symbolize_keys
137
+ .attributes
138
+ .with_indifferent_access
139
+ .slice(*Doorkeeper.config.custom_access_token_attributes)
140
+ .symbolize_keys
138
141
  end
139
142
  end
140
143
  end
@@ -88,7 +88,7 @@ module Doorkeeper
88
88
  #
89
89
  # @param other The set of scopes to filter
90
90
  def allowed(other)
91
- filtered_scopes = other.select { |scope| self.exists?(scope) }
91
+ filtered_scopes = other.select { |scope| exists?(scope) }
92
92
  self.class.from_array(filtered_scopes)
93
93
  end
94
94
 
@@ -115,7 +115,8 @@ module Doorkeeper
115
115
  return false if allowed_pattern[0] != request_pattern[0]
116
116
  return false if allowed_pattern[1].blank?
117
117
  return false if request_pattern[1].blank?
118
- return true if allowed_pattern[1] == DYNAMIC_SCOPE_WILDCARD && allowed_pattern[1].present?
118
+
119
+ return true if allowed_pattern[1] == DYNAMIC_SCOPE_WILDCARD
119
120
 
120
121
  allowed_pattern[1] == request_pattern[1]
121
122
  end
@@ -102,14 +102,18 @@ module Doorkeeper
102
102
 
103
103
  # 2.2. Introspection Response
104
104
  def success_response
105
- customize_response(
105
+ response = {
106
106
  active: true,
107
107
  scope: @token.scopes_string,
108
108
  client_id: @token.try(:application).try(:uid),
109
109
  token_type: @token.token_type,
110
- exp: @token.expires_at.to_i,
111
110
  iat: @token.created_at.to_i,
112
- )
111
+ }
112
+ # `exp` is OPTIONAL per RFC 7662 §2.2; omit it for non-expiring tokens
113
+ # so clients don't interpret `0` as "expired at 1970-01-01".
114
+ response[:exp] = @token.expires_at.to_i if @token.expires_at
115
+
116
+ customize_response(response)
113
117
  end
114
118
 
115
119
  # If the introspection call is properly authorized but the token is not
@@ -73,15 +73,10 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
73
73
  if supports_expiration_time_math?
74
74
  # have not reached the expiration time or it never expires
75
75
  relation.where("#{expiration_time_sql} > ?", Time.now.utc).or(
76
- relation.where(expires_in: nil)
76
+ relation.where(expires_in: nil),
77
77
  )
78
78
  else
79
- ::Kernel.warn <<~WARNING.squish
80
- [DOORKEEPER] Doorkeeper doesn't support expiration time math for your database adapter (#{adapter_name}).
81
- Please add a class method `custom_expiration_time_sql` for your AccessToken class/mixin to provide a custom
82
- SQL expression to calculate access token expiration time. See lib/doorkeeper/orm/active_record/mixins/access_token.rb
83
- for more details.
84
- WARNING
79
+ ::Kernel.warn(::Doorkeeper::Models::ExpirationTimeSqlMath::WARNING_MESSAGE)
85
80
 
86
81
  relation
87
82
  end
@@ -24,12 +24,19 @@ module Doorkeeper
24
24
  # Clears expired records
25
25
  def clean_expired(ttl)
26
26
  table = @base_scope.arel_table
27
+ model_class = @base_scope.is_a?(::ActiveRecord::Relation) ? @base_scope.klass : @base_scope
27
28
 
28
- @base_scope
29
+ scope = @base_scope
29
30
  .where.not(expires_in: nil)
30
31
  .where(table[:created_at].lt(Time.current - ttl))
31
- .where(table[:created_at] + table[:expires_in].lt(Time.current))
32
- .in_batches(&:delete_all)
32
+
33
+ if model_class.respond_to?(:supports_expiration_time_math?) && model_class.supports_expiration_time_math?
34
+ scope = scope.where("#{model_class.expiration_time_sql} < ?", Time.current)
35
+ else
36
+ ::Kernel.warn(::Doorkeeper::Models::ExpirationTimeSqlMath::WARNING_MESSAGE)
37
+ end
38
+
39
+ scope.in_batches(&:delete_all)
33
40
  end
34
41
  end
35
42
  end
@@ -33,12 +33,16 @@ module Doorkeeper
33
33
  end
34
34
 
35
35
  def self.initialize_configured_associations
36
- if Doorkeeper.config.enable_application_owner?
37
- Doorkeeper.config.application_model.include ::Doorkeeper::Models::Ownership
38
- end
36
+ # NOTE: on_load block is instance_exec'd on ActiveRecord::Base,
37
+ # so use fully qualified references (e.g. Doorkeeper.config).
38
+ ActiveSupport.on_load(:active_record) do
39
+ if Doorkeeper.config.enable_application_owner?
40
+ Doorkeeper.config.application_model.include ::Doorkeeper::Models::Ownership
41
+ end
39
42
 
40
- Doorkeeper.config.access_grant_model.include ::Doorkeeper::Models::PolymorphicResourceOwner::ForAccessGrant
41
- Doorkeeper.config.access_token_model.include ::Doorkeeper::Models::PolymorphicResourceOwner::ForAccessToken
43
+ Doorkeeper.config.access_grant_model.include ::Doorkeeper::Models::PolymorphicResourceOwner::ForAccessGrant
44
+ Doorkeeper.config.access_token_model.include ::Doorkeeper::Models::PolymorphicResourceOwner::ForAccessToken
45
+ end
42
46
  end
43
47
  end
44
48
  end
@@ -54,7 +54,7 @@ module Doorkeeper
54
54
  controller: mapping[:controllers],
55
55
  ) do
56
56
  routes.get native_authorization_code_route, action: :show, on: :member
57
- routes.get '/', action: :new, on: :member
57
+ routes.get "/", action: :new, on: :member
58
58
  end
59
59
  end
60
60
 
@@ -5,7 +5,7 @@ module Doorkeeper
5
5
  # Semantic versioning
6
6
  MAJOR = 5
7
7
  MINOR = 9
8
- TINY = 0
8
+ TINY = 1
9
9
  PRE = nil
10
10
 
11
11
  # Full version number
data/lib/doorkeeper.rb CHANGED
@@ -154,6 +154,16 @@ module Doorkeeper
154
154
  end
155
155
  end
156
156
 
157
+ def setup_filter_parameters
158
+ return unless defined?(::Rails) && ::Rails.application && configured?
159
+
160
+ parameters = %w[client_secret authentication_token access_token refresh_token]
161
+ parameters << "code" if configuration.grant_flows.include?("authorization_code")
162
+ filter = /^(#{Regexp.union(parameters)})$/
163
+ filter_params = ::Rails.application.config.filter_parameters
164
+ filter_params << filter unless filter_params.include?(filter)
165
+ end
166
+
157
167
  def setup_orm_adapter
158
168
  @orm_adapter = "doorkeeper/orm/#{configuration.orm}".classify.constantize
159
169
  rescue NameError => e
metadata CHANGED
@@ -1,17 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doorkeeper
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.9.0
4
+ version: 5.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felipe Elias Philipp
8
8
  - Tute Costa
9
9
  - Jon Moss
10
10
  - Nikita Bulai
11
- autorequire:
12
11
  bindir: bin
13
12
  cert_chain: []
14
- date: 2026-03-04 00:00:00.000000000 Z
13
+ date: 1980-01-02 00:00:00.000000000 Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
16
  name: railties
@@ -330,7 +329,6 @@ metadata:
330
329
  bug_tracker_uri: https://github.com/doorkeeper-gem/doorkeeper/issues
331
330
  documentation_uri: https://doorkeeper.gitbook.io/guides/
332
331
  funding_uri: https://opencollective.com/doorkeeper-gem
333
- post_install_message:
334
332
  rdoc_options: []
335
333
  require_paths:
336
334
  - lib
@@ -345,8 +343,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
345
343
  - !ruby/object:Gem::Version
346
344
  version: '0'
347
345
  requirements: []
348
- rubygems_version: 3.5.15
349
- signing_key:
346
+ rubygems_version: 4.0.11
350
347
  specification_version: 4
351
348
  summary: OAuth 2 provider for Rails and Grape
352
349
  test_files: []