doorkeeper 5.5.2 → 5.6.6

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +76 -8
  3. data/README.md +16 -15
  4. data/app/controllers/doorkeeper/authorizations_controller.rb +21 -7
  5. data/app/controllers/doorkeeper/tokens_controller.rb +11 -8
  6. data/app/helpers/doorkeeper/dashboard_helper.rb +1 -1
  7. data/app/views/doorkeeper/authorizations/error.html.erb +3 -1
  8. data/app/views/doorkeeper/authorizations/new.html.erb +16 -16
  9. data/config/locales/en.yml +3 -0
  10. data/lib/doorkeeper/config/abstract_builder.rb +1 -1
  11. data/lib/doorkeeper/config/validations.rb +3 -3
  12. data/lib/doorkeeper/config.rb +44 -54
  13. data/lib/doorkeeper/engine.rb +10 -3
  14. data/lib/doorkeeper/helpers/controller.rb +1 -1
  15. data/lib/doorkeeper/models/access_grant_mixin.rb +1 -1
  16. data/lib/doorkeeper/models/access_token_mixin.rb +7 -7
  17. data/lib/doorkeeper/models/concerns/expiration_time_sql_math.rb +88 -0
  18. data/lib/doorkeeper/models/concerns/polymorphic_resource_owner.rb +30 -0
  19. data/lib/doorkeeper/oauth/authorization/code.rb +7 -1
  20. data/lib/doorkeeper/oauth/authorization/token.rb +7 -1
  21. data/lib/doorkeeper/oauth/authorization_code_request.rb +17 -7
  22. data/lib/doorkeeper/oauth/base_request.rb +11 -10
  23. data/lib/doorkeeper/oauth/client_credentials/creator.rb +10 -13
  24. data/lib/doorkeeper/oauth/client_credentials/validator.rb +1 -2
  25. data/lib/doorkeeper/oauth/error_response.rb +1 -2
  26. data/lib/doorkeeper/oauth/forbidden_token_response.rb +2 -1
  27. data/lib/doorkeeper/oauth/helpers/unique_token.rb +2 -2
  28. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +4 -4
  29. data/lib/doorkeeper/oauth/password_access_token_request.rb +3 -3
  30. data/lib/doorkeeper/oauth/pre_authorization.rb +11 -10
  31. data/lib/doorkeeper/oauth/refresh_token_request.rb +13 -5
  32. data/lib/doorkeeper/oauth/token_introspection.rb +4 -4
  33. data/lib/doorkeeper/oauth/token_response.rb +1 -2
  34. data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +1 -6
  35. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +22 -4
  36. data/lib/doorkeeper/orm/active_record/mixins/application.rb +13 -1
  37. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +2 -2
  38. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +5 -2
  39. data/lib/doorkeeper/orm/active_record.rb +30 -37
  40. data/lib/doorkeeper/rails/routes.rb +12 -3
  41. data/lib/doorkeeper/rake/setup.rake +0 -5
  42. data/lib/doorkeeper/version.rb +2 -2
  43. data/lib/doorkeeper.rb +73 -5
  44. data/lib/generators/doorkeeper/templates/initializer.rb +25 -7
  45. data/lib/generators/doorkeeper/templates/migration.rb.erb +15 -5
  46. metadata +21 -19
@@ -13,6 +13,7 @@ module Doorkeeper
13
13
  include Models::SecretStorable
14
14
  include Models::Scopes
15
15
  include Models::ResourceOwnerable
16
+ include Models::ExpirationTimeSqlMath
16
17
 
17
18
  module ClassMethods
18
19
  # Returns an instance of the Doorkeeper::AccessToken with
@@ -86,8 +87,9 @@ module Doorkeeper
86
87
  # @return [Doorkeeper::AccessToken, nil] Access Token instance or
87
88
  # nil if matching record was not found
88
89
  #
89
- def matching_token_for(application, resource_owner, scopes)
90
+ def matching_token_for(application, resource_owner, scopes, include_expired: true)
90
91
  tokens = authorized_tokens_for(application&.id, resource_owner)
92
+ tokens = tokens.not_expired unless include_expired
91
93
  find_matching_token(tokens, application, scopes)
92
94
  end
93
95
 
@@ -104,9 +106,7 @@ module Doorkeeper
104
106
  #
105
107
  # ActiveRecord 5.x - 6.x ignores custom ordering so we can't perform a
106
108
  # database sort by created_at, so we need to load all the matching records,
107
- # sort them and find latest one. Probably it would be better to rewrite this
108
- # query using Time math if possible, but we n eed to consider ORM and
109
- # different databases support.
109
+ # sort them and find latest one.
110
110
  #
111
111
  # @param relation [ActiveRecord::Relation]
112
112
  # Access tokens relation
@@ -181,7 +181,7 @@ module Doorkeeper
181
181
  #
182
182
  def find_or_create_for(application:, resource_owner:, scopes:, **token_attributes)
183
183
  if Doorkeeper.config.reuse_access_token
184
- access_token = matching_token_for(application, resource_owner, scopes)
184
+ access_token = matching_token_for(application, resource_owner, scopes, include_expired: false)
185
185
 
186
186
  return access_token if access_token&.reusable?
187
187
  end
@@ -213,7 +213,7 @@ module Doorkeeper
213
213
  # @return [Doorkeeper::AccessToken] new access token
214
214
  #
215
215
  def create_for(application:, resource_owner:, scopes:, **token_attributes)
216
- token_attributes[:application_id] = application&.id
216
+ token_attributes[:application] = application
217
217
  token_attributes[:scopes] = scopes.to_s
218
218
 
219
219
  if Doorkeeper.config.polymorphic_resource_owner?
@@ -279,7 +279,7 @@ module Doorkeeper
279
279
  end
280
280
 
281
281
  # Access Token type: Bearer.
282
- # @see https://tools.ietf.org/html/rfc6750
282
+ # @see https://datatracker.ietf.org/doc/html/rfc6750
283
283
  # The OAuth 2.0 Authorization Framework: Bearer Token Usage
284
284
  #
285
285
  def token_type
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module Models
5
+ module ExpirationTimeSqlMath
6
+ extend ::ActiveSupport::Concern
7
+
8
+ class ExpirationTimeSqlGenerator
9
+ attr_reader :model
10
+
11
+ delegate :table_name, to: :@model
12
+
13
+ def initialize(model)
14
+ @model = model
15
+ end
16
+
17
+ def generate_sql
18
+ raise "`generate_sql` should be overridden for a #{self.class.name}!"
19
+ end
20
+ end
21
+
22
+ class MySqlExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator
23
+ def generate_sql
24
+ Arel.sql("DATE_ADD(#{table_name}.created_at, INTERVAL #{table_name}.expires_in SECOND)")
25
+ end
26
+ end
27
+
28
+ class SqlLiteExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator
29
+ def generate_sql
30
+ Arel.sql("DATETIME(#{table_name}.created_at, '+' || #{table_name}.expires_in || ' SECONDS')")
31
+ end
32
+ end
33
+
34
+ class SqlServerExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator
35
+ def generate_sql
36
+ Arel.sql("DATEADD(second, #{table_name}.expires_in, #{table_name}.created_at) AT TIME ZONE 'UTC'")
37
+ end
38
+ end
39
+
40
+ class OracleExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator
41
+ def generate_sql
42
+ Arel.sql("#{table_name}.created_at + INTERVAL to_char(#{table_name}.expires_in) second")
43
+ end
44
+ end
45
+
46
+ class PostgresExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator
47
+ def generate_sql
48
+ Arel.sql("#{table_name}.created_at + #{table_name}.expires_in * INTERVAL '1 SECOND'")
49
+ end
50
+ end
51
+
52
+ ADAPTERS_MAPPING = {
53
+ "sqlite" => SqlLiteExpirationTimeSqlGenerator,
54
+ "sqlite3" => SqlLiteExpirationTimeSqlGenerator,
55
+ "postgis" => PostgresExpirationTimeSqlGenerator,
56
+ "postgresql" => PostgresExpirationTimeSqlGenerator,
57
+ "mysql" => MySqlExpirationTimeSqlGenerator,
58
+ "mysql2" => MySqlExpirationTimeSqlGenerator,
59
+ "trilogy" => MySqlExpirationTimeSqlGenerator,
60
+ "sqlserver" => SqlServerExpirationTimeSqlGenerator,
61
+ "oracleenhanced" => OracleExpirationTimeSqlGenerator,
62
+ }.freeze
63
+
64
+ module ClassMethods
65
+ def supports_expiration_time_math?
66
+ ADAPTERS_MAPPING.key?(adapter_name.downcase) ||
67
+ respond_to?(:custom_expiration_time_sql)
68
+ end
69
+
70
+ def expiration_time_sql
71
+ if respond_to?(:custom_expiration_time_sql)
72
+ custom_expiration_time_sql
73
+ else
74
+ expiration_time_sql_expression
75
+ end
76
+ end
77
+
78
+ def expiration_time_sql_expression
79
+ ADAPTERS_MAPPING.fetch(adapter_name.downcase).new(self).generate_sql
80
+ end
81
+
82
+ def adapter_name
83
+ ActiveRecord::Base.connection.adapter_name
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module Models
5
+ module PolymorphicResourceOwner
6
+ module ForAccessGrant
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ if Doorkeeper.config.polymorphic_resource_owner?
11
+ belongs_to :resource_owner, polymorphic: true, optional: false
12
+ else
13
+ validates :resource_owner_id, presence: true
14
+ end
15
+ end
16
+ end
17
+
18
+ module ForAccessToken
19
+ extend ActiveSupport::Concern
20
+
21
+ included do
22
+ if Doorkeeper.config.polymorphic_resource_owner?
23
+ belongs_to :resource_owner, polymorphic: true, optional: true
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -45,7 +45,13 @@ module Doorkeeper
45
45
  attributes[:resource_owner_id] = resource_owner.id
46
46
  end
47
47
 
48
- pkce_attributes.merge(attributes)
48
+ pkce_attributes.merge(attributes).merge(custom_attributes)
49
+ end
50
+
51
+ def custom_attributes
52
+ # Custom access token attributes are saved into the access grant,
53
+ # and then included in subsequently generated access tokens.
54
+ @pre_auth.custom_access_token_attributes.to_h.with_indifferent_access
49
55
  end
50
56
 
51
57
  def pkce_attributes
@@ -60,7 +60,7 @@ module Doorkeeper
60
60
  )
61
61
 
62
62
  @token = Doorkeeper.config.access_token_model.find_or_create_for(
63
- application: pre_auth.client,
63
+ application: application,
64
64
  resource_owner: resource_owner,
65
65
  scopes: pre_auth.scopes,
66
66
  expires_in: self.class.access_token_expires_in(Doorkeeper.config, context),
@@ -68,6 +68,12 @@ module Doorkeeper
68
68
  )
69
69
  end
70
70
 
71
+ def application
72
+ return unless pre_auth.client
73
+
74
+ pre_auth.client.is_a?(Doorkeeper.config.application_model) ? pre_auth.client : pre_auth.client.application
75
+ end
76
+
71
77
  def oob_redirect
72
78
  {
73
79
  controller: controller,
@@ -6,7 +6,7 @@ module Doorkeeper
6
6
  validate :params, error: :invalid_request
7
7
  validate :client, error: :invalid_client
8
8
  validate :grant, error: :invalid_grant
9
- # @see https://tools.ietf.org/html/rfc6749#section-5.2
9
+ # @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
10
10
  validate :redirect_uri, error: :invalid_grant
11
11
  validate :code_verifier, error: :invalid_grant
12
12
 
@@ -35,6 +35,7 @@ module Doorkeeper
35
35
  grant.application,
36
36
  resource_owner,
37
37
  grant.scopes,
38
+ custom_token_attributes_with_data,
38
39
  server,
39
40
  )
40
41
  end
@@ -55,11 +56,12 @@ module Doorkeeper
55
56
  end
56
57
 
57
58
  def validate_params
58
- @missing_param = if grant&.uses_pkce? && code_verifier.blank?
59
- :code_verifier
60
- elsif redirect_uri.blank?
61
- :redirect_uri
62
- end
59
+ @missing_param =
60
+ if grant&.uses_pkce? && code_verifier.blank?
61
+ :code_verifier
62
+ elsif redirect_uri.blank?
63
+ :redirect_uri
64
+ end
63
65
 
64
66
  @missing_param.nil?
65
67
  end
@@ -97,7 +99,15 @@ module Doorkeeper
97
99
  end
98
100
 
99
101
  def generate_code_challenge(code_verifier)
100
- server_config.access_grant_model.generate_code_challenge(code_verifier)
102
+ Doorkeeper.config.access_grant_model.generate_code_challenge(code_verifier)
103
+ end
104
+
105
+ def custom_token_attributes_with_data
106
+ grant
107
+ .attributes
108
+ .with_indifferent_access
109
+ .slice(*Doorkeeper.config.custom_access_token_attributes)
110
+ .symbolize_keys
101
111
  end
102
112
  end
103
113
  end
@@ -26,27 +26,28 @@ module Doorkeeper
26
26
  @scopes ||= build_scopes
27
27
  end
28
28
 
29
- def find_or_create_access_token(client, resource_owner, scopes, server)
29
+ def find_or_create_access_token(client, resource_owner, scopes, custom_attributes, server)
30
30
  context = Authorization::Token.build_context(client, grant_type, scopes, resource_owner)
31
- @access_token = server_config.access_token_model.find_or_create_for(
32
- application: client,
31
+ application = client.is_a?(Doorkeeper.config.application_model) ? client : client&.application
32
+
33
+ token_attributes = {
34
+ application: application,
33
35
  resource_owner: resource_owner,
34
36
  scopes: scopes,
35
37
  expires_in: Authorization::Token.access_token_expires_in(server, context),
36
38
  use_refresh_token: Authorization::Token.refresh_token_enabled?(server, context),
37
- )
39
+ }
40
+
41
+ @access_token =
42
+ Doorkeeper.config.access_token_model.find_or_create_for(**token_attributes.merge(custom_attributes))
38
43
  end
39
44
 
40
45
  def before_successful_response
41
- server_config.before_successful_strategy_response.call(self)
46
+ Doorkeeper.config.before_successful_strategy_response.call(self)
42
47
  end
43
48
 
44
49
  def after_successful_response
45
- server_config.after_successful_strategy_response.call(self, @response)
46
- end
47
-
48
- def server_config
49
- Doorkeeper.config
50
+ Doorkeeper.config.after_successful_strategy_response.call(self, @response)
50
51
  end
51
52
 
52
53
  private
@@ -8,13 +8,14 @@ module Doorkeeper
8
8
  existing_token = nil
9
9
 
10
10
  if lookup_existing_token?
11
- existing_token = find_existing_token_for(client, scopes)
12
- return existing_token if server_config.reuse_access_token && existing_token&.reusable?
11
+ existing_token = find_active_existing_token_for(client, scopes)
12
+ return existing_token if Doorkeeper.config.reuse_access_token && existing_token&.reusable?
13
13
  end
14
14
 
15
15
  with_revocation(existing_token: existing_token) do
16
- server_config.access_token_model.find_or_create_for(
17
- application: client,
16
+ application = client.is_a?(Doorkeeper.config.application_model) ? client : client&.application
17
+ Doorkeeper.config.access_token_model.create_for(
18
+ application: application,
18
19
  resource_owner: nil,
19
20
  scopes: scopes,
20
21
  **attributes,
@@ -25,7 +26,7 @@ module Doorkeeper
25
26
  private
26
27
 
27
28
  def with_revocation(existing_token:)
28
- if existing_token && server_config.revoke_previous_client_credentials_token?
29
+ if existing_token && Doorkeeper.config.revoke_previous_client_credentials_token?
29
30
  existing_token.with_lock do
30
31
  raise Errors::DoorkeeperError, :invalid_token_reuse if existing_token.revoked?
31
32
 
@@ -39,16 +40,12 @@ module Doorkeeper
39
40
  end
40
41
 
41
42
  def lookup_existing_token?
42
- server_config.reuse_access_token ||
43
- server_config.revoke_previous_client_credentials_token?
43
+ Doorkeeper.config.reuse_access_token ||
44
+ Doorkeeper.config.revoke_previous_client_credentials_token?
44
45
  end
45
46
 
46
- def find_existing_token_for(client, scopes)
47
- server_config.access_token_model.matching_token_for(client, nil, scopes)
48
- end
49
-
50
- def server_config
51
- Doorkeeper.config
47
+ def find_active_existing_token_for(client, scopes)
48
+ Doorkeeper.config.access_token_model.matching_token_for(client, nil, scopes, include_expired: false)
52
49
  end
53
50
  end
54
51
  end
@@ -35,13 +35,12 @@ module Doorkeeper
35
35
  end
36
36
 
37
37
  def validate_scopes
38
- return true if @request.scopes.blank?
39
-
40
38
  application_scopes = if @client.present?
41
39
  @client.application.scopes
42
40
  else
43
41
  ""
44
42
  end
43
+ return true if @request.scopes.blank? && application_scopes.blank?
45
44
 
46
45
  ScopeChecker.valid?(
47
46
  scope_str: @request.scopes.to_s,
@@ -55,8 +55,7 @@ module Doorkeeper
55
55
 
56
56
  def headers
57
57
  {
58
- "Cache-Control" => "no-store",
59
- "Pragma" => "no-cache",
58
+ "Cache-Control" => "no-store, no-cache",
60
59
  "Content-Type" => "application/json; charset=utf-8",
61
60
  "WWW-Authenticate" => authenticate_info,
62
61
  }
@@ -23,7 +23,8 @@ module Doorkeeper
23
23
  end
24
24
 
25
25
  def description
26
- @description ||= @scopes.map { |s| I18n.t(s, scope: %i[doorkeeper scopes]) }.join("\n")
26
+ @description ||= I18n.t("doorkeeper.errors.messages.forbidden_token.missing_scope",
27
+ oauth_scopes: @scopes.map(&:to_s).join(" "),)
27
28
  end
28
29
 
29
30
  protected
@@ -11,8 +11,8 @@ module Doorkeeper
11
11
  # Access Token value must be 1*VSCHAR or
12
12
  # 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="
13
13
  #
14
- # @see https://tools.ietf.org/html/rfc6749#appendix-A.12
15
- # @see https://tools.ietf.org/html/rfc6750#section-2.1
14
+ # @see https://datatracker.ietf.org/doc/html/rfc6749#appendix-A.12
15
+ # @see https://datatracker.ietf.org/doc/html/rfc6750#section-2.1
16
16
  #
17
17
  generator = options.delete(:generator) || SecureRandom.method(default_generator_method)
18
18
  token_size = options.delete(:size) || 32
@@ -28,7 +28,7 @@ module Doorkeeper
28
28
  end
29
29
 
30
30
  # RFC8252, Paragraph 7.3
31
- # @see https://tools.ietf.org/html/rfc8252#section-7.3
31
+ # @see https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
32
32
  if loopback_uri?(url) && loopback_uri?(client_url)
33
33
  url.port = nil
34
34
  client_url.port = nil
@@ -61,9 +61,9 @@ module Doorkeeper
61
61
  end
62
62
 
63
63
  def self.valid_scheme?(uri)
64
- return false if uri.scheme.nil?
64
+ return false if uri.scheme.blank?
65
65
 
66
- %w[localhost].include?(uri.scheme) == false
66
+ %w[localhost].exclude?(uri.scheme)
67
67
  end
68
68
 
69
69
  def self.hypertext_scheme?(uri)
@@ -71,7 +71,7 @@ module Doorkeeper
71
71
  end
72
72
 
73
73
  def self.iff_host?(uri)
74
- !(hypertext_scheme?(uri) && uri.host.nil?)
74
+ !(hypertext_scheme?(uri) && uri.host.blank?)
75
75
  end
76
76
 
77
77
  def self.oob_uri?(uri)
@@ -25,7 +25,7 @@ module Doorkeeper
25
25
  private
26
26
 
27
27
  def before_successful_response
28
- find_or_create_access_token(client, resource_owner, scopes, server)
28
+ find_or_create_access_token(client, resource_owner, scopes, {}, server)
29
29
  super
30
30
  end
31
31
 
@@ -57,7 +57,7 @@ module Doorkeeper
57
57
  #
58
58
  # o authenticate the client if client authentication is included,
59
59
  #
60
- # @see https://tools.ietf.org/html/rfc6749#section-4.3
60
+ # @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.3
61
61
  #
62
62
  def validate_client
63
63
  if Doorkeeper.config.skip_client_authentication_for_password_grant
@@ -68,7 +68,7 @@ module Doorkeeper
68
68
  end
69
69
 
70
70
  def validate_client_supports_grant_flow
71
- server_config.allow_grant_flow_for_client?(grant_type, client&.application)
71
+ Doorkeeper.config.allow_grant_flow_for_client?(grant_type, client&.application)
72
72
  end
73
73
  end
74
74
  end
@@ -18,19 +18,20 @@ module Doorkeeper
18
18
 
19
19
  attr_reader :client, :code_challenge, :code_challenge_method, :missing_param,
20
20
  :redirect_uri, :resource_owner, :response_type, :state,
21
- :authorization_response_flow, :response_mode
21
+ :authorization_response_flow, :response_mode, :custom_access_token_attributes
22
22
 
23
23
  def initialize(server, parameters = {}, resource_owner = nil)
24
- @server = server
25
- @client_id = parameters[:client_id]
26
- @response_type = parameters[:response_type]
27
- @response_mode = parameters[:response_mode]
28
- @redirect_uri = parameters[:redirect_uri]
29
- @scope = parameters[:scope]
30
- @state = parameters[:state]
31
- @code_challenge = parameters[:code_challenge]
24
+ @server = server
25
+ @client_id = parameters[:client_id]
26
+ @response_type = parameters[:response_type]
27
+ @response_mode = parameters[:response_mode]
28
+ @redirect_uri = parameters[:redirect_uri]
29
+ @scope = parameters[:scope]
30
+ @state = parameters[:state]
31
+ @code_challenge = parameters[:code_challenge]
32
32
  @code_challenge_method = parameters[:code_challenge_method]
33
- @resource_owner = resource_owner
33
+ @resource_owner = resource_owner
34
+ @custom_access_token_attributes = parameters.slice(*Doorkeeper.config.custom_access_token_attributes)
34
35
  end
35
36
 
36
37
  def authorizable?
@@ -26,7 +26,7 @@ module Doorkeeper
26
26
  private
27
27
 
28
28
  def load_client(credentials)
29
- server_config.application_model.by_uid_and_secret(credentials.uid, credentials.secret)
29
+ Doorkeeper.config.application_model.by_uid_and_secret(credentials.uid, credentials.secret)
30
30
  end
31
31
 
32
32
  def before_successful_response
@@ -41,7 +41,7 @@ module Doorkeeper
41
41
  end
42
42
 
43
43
  def refresh_token_revoked_on_use?
44
- server_config.access_token_model.refresh_token_revoked_on_use?
44
+ Doorkeeper.config.access_token_model.refresh_token_revoked_on_use?
45
45
  end
46
46
 
47
47
  def default_scopes
@@ -49,7 +49,7 @@ module Doorkeeper
49
49
  end
50
50
 
51
51
  def create_access_token
52
- attributes = {}
52
+ attributes = {}.merge(custom_token_attributes_with_data)
53
53
 
54
54
  resource_owner =
55
55
  if Doorkeeper.config.polymorphic_resource_owner?
@@ -75,7 +75,7 @@ module Doorkeeper
75
75
  # Here we assume that TTL of the token received after refreshing should be
76
76
  # the same as that of the original token.
77
77
  #
78
- @access_token = server_config.access_token_model.create_for(
78
+ @access_token = Doorkeeper.config.access_token_model.create_for(
79
79
  application: refresh_token.application,
80
80
  resource_owner: resource_owner,
81
81
  scopes: scopes,
@@ -101,7 +101,7 @@ module Doorkeeper
101
101
  client.present?
102
102
  end
103
103
 
104
- # @see https://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-1.5
104
+ # @see https://datatracker.ietf.org/doc/html/rfc6749#section-1.5
105
105
  #
106
106
  def validate_client_match
107
107
  return true if refresh_token.application_id.blank?
@@ -119,6 +119,14 @@ module Doorkeeper
119
119
  true
120
120
  end
121
121
  end
122
+
123
+ def custom_token_attributes_with_data
124
+ refresh_token
125
+ .attributes
126
+ .with_indifferent_access
127
+ .slice(*Doorkeeper.config.custom_access_token_attributes)
128
+ .symbolize_keys
129
+ end
122
130
  end
123
131
  end
124
132
  end
@@ -4,7 +4,7 @@ module Doorkeeper
4
4
  module OAuth
5
5
  # RFC7662 OAuth 2.0 Token Introspection
6
6
  #
7
- # @see https://tools.ietf.org/html/rfc7662
7
+ # @see https://datatracker.ietf.org/doc/html/rfc7662
8
8
  class TokenIntrospection
9
9
  def initialize(server, token)
10
10
  @server = server
@@ -107,7 +107,7 @@ module Doorkeeper
107
107
  # authorization server SHOULD NOT include any additional information
108
108
  # about an inactive token, including why the token is inactive.
109
109
  #
110
- # @see https://tools.ietf.org/html/rfc7662 2.2. Introspection Response
110
+ # @see https://datatracker.ietf.org/doc/html/rfc7662 2.2. Introspection Response
111
111
  #
112
112
  def failure_response
113
113
  {
@@ -134,7 +134,7 @@ module Doorkeeper
134
134
  # Since resource servers using token introspection rely on the
135
135
  # authorization server to determine the state of a token, the
136
136
  # authorization server MUST perform all applicable checks against a
137
- # token's state. For instance, these tests include the following:
137
+ # token's state. For instance, these tests include the following:
138
138
  #
139
139
  # o If the token can expire, the authorization server MUST determine
140
140
  # whether or not the token has expired.
@@ -186,7 +186,7 @@ module Doorkeeper
186
186
  # Provides context (controller) and token for generating developer-specific
187
187
  # response.
188
188
  #
189
- # @see https://tools.ietf.org/html/rfc7662#section-2.2
189
+ # @see https://datatracker.ietf.org/doc/html/rfc7662#section-2.2
190
190
  #
191
191
  def customize_response(response)
192
192
  customized_response = Doorkeeper.config.custom_introspection_response.call(
@@ -26,8 +26,7 @@ module Doorkeeper
26
26
 
27
27
  def headers
28
28
  {
29
- "Cache-Control" => "no-store",
30
- "Pragma" => "no-cache",
29
+ "Cache-Control" => "no-store, no-cache",
31
30
  "Content-Type" => "application/json; charset=utf-8",
32
31
  }
33
32
  end
@@ -6,6 +6,7 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
6
6
 
7
7
  included do
8
8
  self.table_name = compute_doorkeeper_table_name
9
+ self.strict_loading_by_default = false if respond_to?(:strict_loading_by_default)
9
10
 
10
11
  include ::Doorkeeper::AccessGrantMixin
11
12
 
@@ -13,12 +14,6 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
13
14
  optional: true,
14
15
  inverse_of: :access_grants
15
16
 
16
- if Doorkeeper.config.polymorphic_resource_owner?
17
- belongs_to :resource_owner, polymorphic: true, optional: false
18
- else
19
- validates :resource_owner_id, presence: true
20
- end
21
-
22
17
  validates :application_id,
23
18
  :token,
24
19
  :expires_in,
@@ -6,6 +6,7 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
6
6
 
7
7
  included do
8
8
  self.table_name = compute_doorkeeper_table_name
9
+ self.strict_loading_by_default = false if respond_to?(:strict_loading_by_default)
9
10
 
10
11
  include ::Doorkeeper::AccessTokenMixin
11
12
 
@@ -13,10 +14,6 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
13
14
  inverse_of: :access_tokens,
14
15
  optional: true
15
16
 
16
- if Doorkeeper.config.polymorphic_resource_owner?
17
- belongs_to :resource_owner, polymorphic: true, optional: true
18
- end
19
-
20
17
  validates :token, presence: true, uniqueness: { case_sensitive: true }
21
18
  validates :refresh_token, uniqueness: { case_sensitive: true }, if: :use_refresh_token?
22
19
 
@@ -47,6 +44,27 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
47
44
  column_names.include?("previous_refresh_token")
48
45
  end
49
46
 
47
+ # Returns non-expired and non-revoked access tokens
48
+ def not_expired
49
+ relation = where(revoked_at: nil)
50
+
51
+ if supports_expiration_time_math?
52
+ # have not reached the expiration time or it never expires
53
+ relation.where("#{expiration_time_sql} > ?", Time.now.utc).or(
54
+ relation.where(expires_in: nil)
55
+ )
56
+ else
57
+ ::Kernel.warn <<~WARNING.squish
58
+ [DOORKEEPER] Doorkeeper doesn't support expiration time math for your database adapter (#{adapter_name}).
59
+ Please add a class method `custom_expiration_time_sql` for your AccessToken class/mixin to provide a custom
60
+ SQL expression to calculate access token expiration time. See lib/doorkeeper/orm/active_record/mixins/access_token.rb
61
+ for more details.
62
+ WARNING
63
+
64
+ relation
65
+ end
66
+ end
67
+
50
68
  private
51
69
 
52
70
  def compute_doorkeeper_table_name