doorkeeper 5.9.3 → 6.0.0.beta1

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -0
  3. data/README.md +1 -0
  4. data/app/controllers/doorkeeper/metadata_controller.rb +20 -0
  5. data/app/controllers/doorkeeper/tokens_controller.rb +10 -1
  6. data/app/views/doorkeeper/authorizations/new.html.erb +6 -0
  7. data/config/locales/en.yml +1 -0
  8. data/lib/doorkeeper/client_authentication/credentials.rb +11 -0
  9. data/lib/doorkeeper/client_authentication/fallback_method.rb +19 -0
  10. data/lib/doorkeeper/client_authentication/legacy_callable.rb +46 -0
  11. data/lib/doorkeeper/client_authentication/method.rb +23 -0
  12. data/lib/doorkeeper/client_authentication/registry.rb +47 -0
  13. data/lib/doorkeeper/client_authentication.rb +93 -0
  14. data/lib/doorkeeper/config/option.rb +1 -1
  15. data/lib/doorkeeper/config/validations.rb +162 -1
  16. data/lib/doorkeeper/config.rb +114 -9
  17. data/lib/doorkeeper/errors.rb +14 -0
  18. data/lib/doorkeeper/helpers/controller.rb +2 -1
  19. data/lib/doorkeeper/models/access_token_mixin.rb +48 -8
  20. data/lib/doorkeeper/models/application_mixin.rb +14 -6
  21. data/lib/doorkeeper/models/concerns/secret_storable.rb +10 -1
  22. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +11 -0
  23. data/lib/doorkeeper/oauth/authorization_code_request.rb +4 -1
  24. data/lib/doorkeeper/oauth/client.rb +18 -0
  25. data/lib/doorkeeper/oauth/client_authentication/client_secret_basic.rb +53 -0
  26. data/lib/doorkeeper/oauth/client_authentication/client_secret_post.rb +29 -0
  27. data/lib/doorkeeper/oauth/client_authentication/none.rb +32 -0
  28. data/lib/doorkeeper/oauth/client_credentials/creator.rb +4 -1
  29. data/lib/doorkeeper/oauth/code_response.rb +16 -2
  30. data/lib/doorkeeper/oauth/error_response.rb +11 -2
  31. data/lib/doorkeeper/oauth/metadata_response.rb +157 -0
  32. data/lib/doorkeeper/oauth/pre_authorization.rb +12 -5
  33. data/lib/doorkeeper/oauth/token_introspection.rb +27 -7
  34. data/lib/doorkeeper/orm/active_record/mixins/application.rb +2 -2
  35. data/lib/doorkeeper/rails/routes/mapping.rb +1 -0
  36. data/lib/doorkeeper/rails/routes.rb +6 -0
  37. data/lib/doorkeeper/request.rb +59 -0
  38. data/lib/doorkeeper/server.rb +5 -2
  39. data/lib/doorkeeper/version.rb +4 -4
  40. data/lib/doorkeeper.rb +8 -4
  41. data/lib/generators/doorkeeper/templates/initializer.rb +66 -6
  42. metadata +12 -2
  43. data/lib/doorkeeper/oauth/client/credentials.rb +0 -34
@@ -5,7 +5,7 @@ module Doorkeeper
5
5
  class ErrorResponse < BaseResponse
6
6
  include OAuth::Helpers
7
7
 
8
- NON_REDIRECTABLE_STATES = %i[invalid_redirect_uri invalid_client unauthorized_client].freeze
8
+ NON_REDIRECTABLE_STATES = %i[invalid_redirect_uri invalid_client].freeze
9
9
 
10
10
  def self.from_request(request, attributes = {})
11
11
  new(
@@ -38,6 +38,7 @@ module Doorkeeper
38
38
  @exception_class = attributes[:exception_class]
39
39
  @redirect_uri = attributes[:redirect_uri]
40
40
  @response_on_fragment = attributes[:response_on_fragment]
41
+ @issuer = attributes[:issuer]
41
42
  end
42
43
 
43
44
  def body
@@ -45,11 +46,19 @@ module Doorkeeper
45
46
  error: name,
46
47
  error_description: description,
47
48
  state: state,
49
+ # RFC 9207 scopes the issuer to authorization error responses sent to
50
+ # the client, i.e. the ones redirected back to its redirect_uri. It is
51
+ # supplied only by the authorization endpoint (never token,
52
+ # introspection or protected resource), and gated on redirectable? so
53
+ # non-redirectable errors (invalid_client, invalid_redirect_uri) and
54
+ # out-of-band flows - which render to the user instead of redirecting
55
+ # to the client - do not carry it. Blank values are dropped below.
56
+ iss: (@issuer if redirectable?),
48
57
  }.reject { |_, v| v.blank? }
49
58
  end
50
59
 
51
60
  def status
52
- if name == :invalid_client || name == :unauthorized_client
61
+ if name == :invalid_client
53
62
  :unauthorized
54
63
  else
55
64
  :bad_request
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module OAuth
5
+ # OAuth 2.0 Authorization Server Metadata response as described in
6
+ # RFC 8414 (https://www.rfc-editor.org/rfc/rfc8414).
7
+ class MetadataResponse < BaseResponse
8
+ def initialize(base_url, url_builder)
9
+ super()
10
+ @base_url = base_url
11
+ @url_builder = url_builder
12
+ end
13
+
14
+ def body
15
+ @body ||= begin
16
+ data = {
17
+ # presence: a blank issuer (e.g. an unset env var) must fall back
18
+ # to base_url, matching the issuer.present? condition below so the
19
+ # two fields cannot disagree.
20
+ issuer: issuer.presence || @base_url,
21
+ # Only advertise endpoints whose controllers are installed. A
22
+ # controller disabled through skip_controllers has no routes mapping,
23
+ # so endpoint_for resolves it to nil and it is dropped below.
24
+ authorization_endpoint: authorization_endpoint,
25
+ token_endpoint: token_endpoint,
26
+ revocation_endpoint: revocation_endpoint,
27
+ introspection_endpoint: (introspection_endpoint if introspection_enabled?),
28
+ scopes_supported: scopes_supported,
29
+ response_types_supported: response_types_supported,
30
+ response_modes_supported: response_modes_supported,
31
+ grant_types_supported: grant_types_supported,
32
+ token_endpoint_auth_methods_supported: token_endpoint_auth_methods_supported,
33
+ code_challenge_methods_supported: code_challenge_methods_supported,
34
+ # RFC 9207: true only when an issuer is configured, matching the
35
+ # condition under which the iss parameter is emitted. false survives
36
+ # the compaction below, so it is advertised explicitly.
37
+ authorization_response_iss_parameter_supported: config.issuer.present?,
38
+ }
39
+ data.compact!
40
+
41
+ # userinfo_endpoint is intentionally advertised as null for backwards
42
+ # compatibility; it is meant to be populated through custom_metadata
43
+ # (e.g. by an OIDC extension), so it must survive the compaction above.
44
+ data[:userinfo_endpoint] = userinfo_endpoint
45
+
46
+ data.merge(custom_metadata)
47
+ end
48
+ end
49
+
50
+ def status
51
+ :ok
52
+ end
53
+
54
+ def headers
55
+ {
56
+ "Cache-Control" => "public",
57
+ "Content-Type" => "application/json; charset=utf-8",
58
+ }
59
+ end
60
+
61
+ private
62
+
63
+ def config
64
+ @config ||= Doorkeeper.configuration
65
+ end
66
+
67
+ def url_for(**args)
68
+ @url_builder.call(**args)
69
+ end
70
+
71
+ # Resolve the URL for the given route group, or nil when the group has no
72
+ # routes mapping (i.e. the controller was disabled via skip_controllers).
73
+ def endpoint_for(group, action:)
74
+ mapping = Doorkeeper::Rails::Routes.mapping[group]
75
+ return unless mapping
76
+
77
+ # The leading slash anchors the controller name: without it, Rails
78
+ # resolves the name relative to the current (doorkeeper/metadata)
79
+ # namespace whenever their segment depths differ, e.g. a configured
80
+ # `controllers tokens: "custom_tokens"` would be looked up as
81
+ # "doorkeeper/custom_tokens" and raise ActionController::UrlGenerationError.
82
+ url_for(controller: "/#{mapping[:controllers]}", action: action)
83
+ end
84
+
85
+ def custom_metadata
86
+ config.custom_metadata.symbolize_keys
87
+ end
88
+
89
+ def issuer
90
+ config.issuer
91
+ end
92
+
93
+ def authorization_endpoint
94
+ endpoint_for(:authorizations, action: "new")
95
+ end
96
+
97
+ def token_endpoint
98
+ endpoint_for(:tokens, action: "create")
99
+ end
100
+
101
+ def userinfo_endpoint
102
+ nil
103
+ end
104
+
105
+ def revocation_endpoint
106
+ endpoint_for(:tokens, action: "revoke")
107
+ end
108
+
109
+ def introspection_endpoint
110
+ endpoint_for(:tokens, action: "introspect")
111
+ end
112
+
113
+ def introspection_enabled?
114
+ Doorkeeper.configured? &&
115
+ !config.allow_token_introspection.is_a?(FalseClass)
116
+ end
117
+
118
+ def scopes_supported
119
+ config.scopes.to_a
120
+ end
121
+
122
+ def response_types_supported
123
+ config.authorization_response_types
124
+ end
125
+
126
+ def response_modes_supported
127
+ config.authorization_response_flows.flat_map(&:response_mode_matches).uniq
128
+ end
129
+
130
+ def grant_types_supported
131
+ # RFC 8414 lists token-endpoint grant type values (e.g.
132
+ # "authorization_code", "refresh_token"), so derive them from the
133
+ # flows that actually handle a grant_type and use their grant_type
134
+ # value rather than the configured flow name. This drops
135
+ # response-type-only flows such as :implicit and reports the real
136
+ # grant type for flows whose name differs from it (e.g. a URN).
137
+ config.token_grant_types
138
+ end
139
+
140
+ # The resolved methods (rather than the raw client_authentication names)
141
+ # reflect what the server actually accepts: unregistered names are
142
+ # dropped by the resolver, and a deprecated client_credentials-only
143
+ # configuration is honored as the source of truth. Legacy callable
144
+ # extractors have no registered method name a client could use, so they
145
+ # are not advertised.
146
+ def token_endpoint_auth_methods_supported
147
+ config.client_authentication_methods.filter_map do |method|
148
+ method.name.to_s if Doorkeeper::ClientAuthentication.get(method.name)
149
+ end
150
+ end
151
+
152
+ def code_challenge_methods_supported
153
+ config.pkce_code_challenge_methods_supported
154
+ end
155
+ end
156
+ end
157
+ end
@@ -7,9 +7,9 @@ module Doorkeeper
7
7
 
8
8
  validate :client_id, error: Errors::InvalidRequest
9
9
  validate :client, error: Errors::InvalidClient
10
- validate :client_supports_grant_flow, error: Errors::UnauthorizedClient
11
- validate :resource_owner_authorize_for_client, error: Errors::InvalidClient
12
10
  validate :redirect_uri, error: Errors::InvalidRedirectUri
11
+ validate :client_supports_grant_flow, error: Errors::UnauthorizedClient
12
+ validate :resource_owner_authorize_for_client, error: Errors::AccessDenied
13
13
  validate :params, error: Errors::InvalidRequest
14
14
  validate :response_type, error: Errors::UnsupportedResponseType
15
15
  validate :response_mode, error: Errors::UnsupportedResponseMode
@@ -49,13 +49,21 @@ module Doorkeeper
49
49
  end
50
50
 
51
51
  def error_response
52
+ # RFC 9207: authorization error responses advertise the issuer. Passing
53
+ # the configured issuer here (nil when unset) scopes the iss parameter
54
+ # to the authorization endpoint only.
52
55
  if error == Errors::InvalidRequest
53
56
  OAuth::InvalidRequestResponse.from_request(
54
57
  self,
55
58
  response_on_fragment: response_on_fragment?,
59
+ issuer: Doorkeeper.config.issuer,
56
60
  )
57
61
  else
58
- OAuth::ErrorResponse.from_request(self, response_on_fragment: response_on_fragment?)
62
+ OAuth::ErrorResponse.from_request(
63
+ self,
64
+ response_on_fragment: response_on_fragment?,
65
+ issuer: Doorkeeper.config.issuer,
66
+ )
59
67
  end
60
68
  end
61
69
 
@@ -147,7 +155,6 @@ module Doorkeeper
147
155
 
148
156
  def validate_code_challenge
149
157
  return true unless Doorkeeper.config.force_pkce?
150
- return true if client.confidential
151
158
  return true if code_challenge.present?
152
159
 
153
160
  @invalid_request_reason = :invalid_code_challenge
@@ -180,7 +187,7 @@ module Doorkeeper
180
187
  scope: scope,
181
188
  client_name: client.name,
182
189
  status: I18n.t("doorkeeper.pre_authorization.status"),
183
- }
190
+ }.reverse_merge(custom_access_token_attributes.symbolize_keys)
184
191
  end
185
192
  end
186
193
  end
@@ -8,9 +8,13 @@ module Doorkeeper
8
8
  class TokenIntrospection
9
9
  attr_reader :token, :error, :invalid_request_reason
10
10
 
11
- def initialize(server, token)
11
+ # +token_type+ tells which credential of the token record the caller
12
+ # actually received (:access_token or :refresh_token), so the "active"
13
+ # state and the response describe the presented token per RFC 7662 §2.2.
14
+ def initialize(server, token, token_type: :access_token)
12
15
  @server = server
13
16
  @token = token
17
+ @token_type = token_type
14
18
  end
15
19
 
16
20
  def authorized?
@@ -106,12 +110,19 @@ module Doorkeeper
106
110
  active: true,
107
111
  scope: @token.scopes_string,
108
112
  client_id: @token.try(:application).try(:uid),
109
- token_type: @token.token_type,
110
113
  iat: @token.created_at.to_i,
111
114
  }
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
+ # `token_type` (RFC 6749 §7.1) and `exp` describe the access token:
117
+ # a refresh token is not a Bearer credential and has no expiry of its
118
+ # own, so both are omitted (they are OPTIONAL per RFC 7662 §2.2) when
119
+ # a refresh token is presented.
120
+ unless refresh_token_presented?
121
+ response[:token_type] = @token.token_type
122
+ # `exp` is OPTIONAL per RFC 7662 §2.2; omit it for non-expiring tokens
123
+ # so clients don't interpret `0` as "expired at 1970-01-01".
124
+ response[:exp] = @token.expires_at.to_i if @token.expires_at
125
+ end
115
126
 
116
127
  customize_response(response)
117
128
  end
@@ -176,9 +187,18 @@ module Doorkeeper
176
187
  end
177
188
  end
178
189
 
179
- # Token can be valid only if it is not expired or revoked.
190
+ # The presented token can be valid only if it is not revoked; an access
191
+ # token must additionally be unexpired. A refresh token has no expiry of
192
+ # its own and stays usable at the token endpoint after its paired access
193
+ # token expires, so that expiry is not consulted here.
180
194
  def valid_token?
181
- @token&.accessible?
195
+ return false if @token.blank?
196
+
197
+ refresh_token_presented? ? !@token.revoked? : @token.accessible?
198
+ end
199
+
200
+ def refresh_token_presented?
201
+ @token_type == :refresh_token
182
202
  end
183
203
 
184
204
  def valid_authorized_token?
@@ -116,9 +116,9 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
116
116
 
117
117
  return generator if generator.respond_to?(:generate)
118
118
 
119
- raise Errors::UnableToGenerateToken, "#{generator} does not respond to `.generate`."
119
+ raise Doorkeeper::Errors::UnableToGenerateToken, "#{generator} does not respond to `.generate`."
120
120
  rescue NameError
121
- raise Errors::TokenGeneratorNotFound, "#{generator_name} not found"
121
+ raise Doorkeeper::Errors::TokenGeneratorNotFound, "#{generator_name} not found"
122
122
  end
123
123
 
124
124
  def generate_uid
@@ -11,6 +11,7 @@ module Doorkeeper
11
11
  authorizations: "doorkeeper/authorizations",
12
12
  applications: "doorkeeper/applications",
13
13
  authorized_applications: "doorkeeper/authorized_applications",
14
+ metadata: "doorkeeper/metadata",
14
15
  tokens: "doorkeeper/tokens",
15
16
  token_info: "doorkeeper/token_info",
16
17
  }
@@ -41,10 +41,16 @@ module Doorkeeper
41
41
  map_route(:authorized_applications, :authorized_applications_routes)
42
42
  map_route(:token_info, :token_info_routes)
43
43
  end
44
+
45
+ map_route(:metadata, :metadata_routes)
44
46
  end
45
47
 
46
48
  private
47
49
 
50
+ def metadata_routes(mapping)
51
+ routes.get ".well-known/oauth-authorization-server", controller: mapping[:controllers], action: :show
52
+ end
53
+
48
54
  def authorization_routes(mapping)
49
55
  routes.resource(
50
56
  :authorization,
@@ -3,6 +3,28 @@
3
3
  module Doorkeeper
4
4
  module Request
5
5
  class << self
6
+ # Detect the OAuth client authentication method (RFC 6749 §2.3) that the
7
+ # given request uses. Returns the matching method's strategy (not the
8
+ # registry's Method wrapper), or FallbackMethod when none matches
9
+ # (which authenticates to no credentials).
10
+ #
11
+ # Raises Errors::MultipleClientAuthMethods when the request itself
12
+ # uses more than one client authentication method, since RFC 6749 §2.3
13
+ # forbids that (see +validate_client_authentication!+).
14
+ def client_authentication_method(request)
15
+ validate_client_authentication!(request)
16
+
17
+ authentication_method = client_authentication_methods.detect do |method|
18
+ method.matches_request?(request)
19
+ end
20
+
21
+ if authentication_method
22
+ authentication_method.strategy
23
+ else
24
+ Doorkeeper::ClientAuthentication::FallbackMethod
25
+ end
26
+ end
27
+
6
28
  def authorization_strategy(response_type)
7
29
  grant_flow = authorization_flows.detect do |flow|
8
30
  flow.matches_response_type?(response_type)
@@ -40,6 +62,43 @@ module Doorkeeper
40
62
 
41
63
  private
42
64
 
65
+ # RFC 6749 §2.3 forbids clients to "use more than one authentication
66
+ # method in each request", so the request payload is validated against
67
+ # every *registered* method — regardless of which ones are configured —
68
+ # before any method is selected: a client sending, say, both Basic
69
+ # credentials and body credentials is rejected even when only one of
70
+ # those methods is enabled on the server.
71
+ #
72
+ # Only real authentication mechanisms count towards the limit:
73
+ #
74
+ # * +:none+ is the absence of client authentication (a public client
75
+ # identifying itself with a bare +client_id+), not a mechanism of its
76
+ # own — RFC 7521 §4.2, for example, explicitly allows a +client_id+
77
+ # next to a client assertion.
78
+ # * Deprecated +client_credentials+ callable extractors are
79
+ # configuration adapters rather than registered methods, so they
80
+ # cannot count either; they keep the historical "first extractor that
81
+ # returns a uid wins" selection (see
82
+ # ClientAuthentication::LegacyCallable) for the deprecation window.
83
+ def validate_client_authentication!(request)
84
+ matched = 0
85
+
86
+ Doorkeeper::ClientAuthentication.registered_methods.each_value do |method|
87
+ next if method.name == :none
88
+ next unless method.matches_request?(request)
89
+
90
+ matched += 1
91
+
92
+ # RFC 6749 §2.3 only forbids using more than one method, so bail out
93
+ # on the second match instead of evaluating the remaining methods.
94
+ raise Errors::MultipleClientAuthMethods if matched > 1
95
+ end
96
+ end
97
+
98
+ def client_authentication_methods
99
+ Doorkeeper.configuration.client_authentication_methods
100
+ end
101
+
43
102
  def authorization_flows
44
103
  Doorkeeper.configuration.authorization_response_flows
45
104
  end
@@ -8,6 +8,10 @@ module Doorkeeper
8
8
  @context = context
9
9
  end
10
10
 
11
+ def client_authentication_method_for_request
12
+ Request.client_authentication_method(context.request)
13
+ end
14
+
11
15
  def authorization_request(strategy)
12
16
  klass = Request.authorization_strategy(strategy)
13
17
  klass.new(self)
@@ -37,8 +41,7 @@ module Doorkeeper
37
41
  end
38
42
 
39
43
  def credentials
40
- methods = Doorkeeper.config.client_credentials_methods
41
- @credentials ||= OAuth::Client::Credentials.from_request(context.request, *methods)
44
+ @credentials ||= client_authentication_method_for_request.authenticate(context.request)
42
45
  end
43
46
  end
44
47
  end
@@ -3,10 +3,10 @@
3
3
  module Doorkeeper
4
4
  module VERSION
5
5
  # Semantic versioning
6
- MAJOR = 5
7
- MINOR = 9
8
- TINY = 3
9
- PRE = nil
6
+ MAJOR = 6
7
+ MINOR = 0
8
+ TINY = 0
9
+ PRE = "beta1"
10
10
 
11
11
  # Full version number
12
12
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
data/lib/doorkeeper.rb CHANGED
@@ -7,6 +7,7 @@ require "doorkeeper/engine"
7
7
  #
8
8
  module Doorkeeper
9
9
  autoload :Errors, "doorkeeper/errors"
10
+ autoload :ClientAuthentication, "doorkeeper/client_authentication"
10
11
  autoload :GrantFlow, "doorkeeper/grant_flow"
11
12
  autoload :OAuth, "doorkeeper/oauth"
12
13
  autoload :Rake, "doorkeeper/rake"
@@ -52,6 +53,7 @@ module Doorkeeper
52
53
  autoload :InvalidTokenResponse, "doorkeeper/oauth/invalid_token_response"
53
54
  autoload :InvalidRequestResponse, "doorkeeper/oauth/invalid_request_response"
54
55
  autoload :ForbiddenTokenResponse, "doorkeeper/oauth/forbidden_token_response"
56
+ autoload :MetadataResponse, "doorkeeper/oauth/metadata_response"
55
57
  autoload :NonStandard, "doorkeeper/oauth/nonstandard"
56
58
  autoload :PasswordAccessTokenRequest, "doorkeeper/oauth/password_access_token_request"
57
59
  autoload :PreAuthorization, "doorkeeper/oauth/pre_authorization"
@@ -62,6 +64,12 @@ module Doorkeeper
62
64
  autoload :TokenRequest, "doorkeeper/oauth/token_request"
63
65
  autoload :TokenResponse, "doorkeeper/oauth/token_response"
64
66
 
67
+ module ClientAuthentication
68
+ autoload :None, "doorkeeper/oauth/client_authentication/none"
69
+ autoload :ClientSecretBasic, "doorkeeper/oauth/client_authentication/client_secret_basic"
70
+ autoload :ClientSecretPost, "doorkeeper/oauth/client_authentication/client_secret_post"
71
+ end
72
+
65
73
  module Authorization
66
74
  autoload :Code, "doorkeeper/oauth/authorization/code"
67
75
  autoload :Context, "doorkeeper/oauth/authorization/context"
@@ -69,10 +77,6 @@ module Doorkeeper
69
77
  autoload :URIBuilder, "doorkeeper/oauth/authorization/uri_builder"
70
78
  end
71
79
 
72
- class Client
73
- autoload :Credentials, "doorkeeper/oauth/client/credentials"
74
- end
75
-
76
80
  module ClientCredentials
77
81
  autoload :Validator, "doorkeeper/oauth/client_credentials/validator"
78
82
  autoload :Creator, "doorkeeper/oauth/client_credentials/creator"
@@ -150,6 +150,15 @@ Doorkeeper.configure do
150
150
  # token found for the application, resources owner and/or set of scopes.
151
151
  # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383
152
152
  #
153
+ # Matching considers only the application, resource owner, scopes, custom access token
154
+ # attributes (see +custom_access_token_attributes+) and whether a refresh token is
155
+ # expected — not the authorization request that produced the token. Separate authorization
156
+ # grants for the same combination share a single access token, so concurrent sessions of
157
+ # the same client become interdependent (e.g. refreshing the token in one session revokes
158
+ # it for the others). If you need independent tokens per session or device, keep this
159
+ # option disabled or differentiate the sessions with +custom_access_token_attributes+.
160
+ # See https://github.com/doorkeeper-gem/doorkeeper/issues/1693
161
+ #
153
162
  # You can not enable this option together with +hash_token_secrets+.
154
163
  #
155
164
  # reuse_access_token
@@ -188,8 +197,8 @@ Doorkeeper.configure do
188
197
  #
189
198
  # revoke_previous_authorization_code_token
190
199
 
191
- # Require non-confidential clients to use PKCE when using an authorization code
192
- # to obtain an access_token (disabled by default)
200
+ # Require all clients (including confidential ones) to use PKCE when using an
201
+ # authorization code to obtain an access_token (disabled by default)
193
202
  #
194
203
  # force_pkce
195
204
 
@@ -277,13 +286,18 @@ Doorkeeper.configure do
277
286
  #
278
287
  # enforce_configured_scopes
279
288
 
280
- # Change the way client credentials are retrieved from the request object.
281
- # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
282
- # falls back to the `:client_id` and `:client_secret` params from the `params` object.
289
+ # Configure the OAuth client authentication methods (RFC 6749 §2.3) Doorkeeper
290
+ # will accept and the order in which they are tried. By default it accepts
291
+ # HTTP Basic auth (`client_secret_basic`), credentials in the request body
292
+ # (`client_secret_post`), and public clients with no secret (`none`).
283
293
  # Check out https://github.com/doorkeeper-gem/doorkeeper/wiki/Changing-how-clients-are-authenticated
284
294
  # for more information on customization
285
295
  #
286
- # client_credentials :from_basic, :from_params
296
+ # client_authentication %i[client_secret_basic client_secret_post none]
297
+ #
298
+ # The legacy `client_credentials` option is deprecated; `:from_basic` and
299
+ # `:from_params` are automatically mapped to `:client_secret_basic` and
300
+ # `:client_secret_post`.
287
301
 
288
302
  # Change the way access token is authenticated from the request object.
289
303
  # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
@@ -377,6 +391,9 @@ Doorkeeper.configure do
377
391
  # If not specified, Doorkeeper enables authorization_code and
378
392
  # client_credentials.
379
393
  #
394
+ # The Refresh Token Grant Flow ("refresh_token") doesn't need to be listed
395
+ # here: it is enabled automatically when +use_refresh_token+ is configured.
396
+ #
380
397
  # implicit and password grant flows have risks that you should understand
381
398
  # before enabling:
382
399
  # https://datatracker.ietf.org/doc/html/rfc6819#section-4.4.2
@@ -541,4 +558,47 @@ Doorkeeper.configure do
541
558
  # WWW-Authenticate Realm (default: "Doorkeeper").
542
559
  #
543
560
  # realm "Doorkeeper"
561
+
562
+ # OAuth 2.0 Authorization Server Metadata (RFC 8414).
563
+ #
564
+ # Doorkeeper exposes an authorization server metadata document at
565
+ # `/.well-known/oauth-authorization-server`, built from the configuration
566
+ # above. The two options below let you customize that document.
567
+ #
568
+ # `issuer` is the authorization server's issuer identifier. When left as nil
569
+ # (the default) the request base URL is used for the metadata `issuer` field
570
+ # only. Note the asymmetry: RFC 9207 below is gated on an explicitly
571
+ # configured issuer, so leaving it nil still advertises a metadata `issuer`
572
+ # (the base URL) while `authorization_response_iss_parameter_supported` stays
573
+ # false and no `iss` parameter is emitted.
574
+ #
575
+ # Configuring an issuer also enables RFC 9207 (Authorization Server Issuer
576
+ # Identification): the `iss` parameter is added to the authorization
577
+ # responses redirected back to the client - both successful responses and
578
+ # error responses such as access_denied - and advertised via the
579
+ # `authorization_response_iss_parameter_supported` metadata field. Clients
580
+ # that parse the authorization redirect will start seeing this parameter.
581
+ # Per RFC 8414 and RFC 9207 the value should be an https URL with no query or
582
+ # fragment; a non-compliant value logs a warning at boot but is still used.
583
+ # Prefer a host-only issuer: Doorkeeper serves its metadata only at the root
584
+ # /.well-known/oauth-authorization-server, so a path-bearing issuer (e.g.
585
+ # https://auth.example.com/tenant) is not discoverable by RFC 8414 clients and
586
+ # also logs a warning.
587
+ #
588
+ # issuer "https://auth.example.com"
589
+ #
590
+ # `custom_metadata` is a Hash that is merged into the metadata response. Use
591
+ # it to advertise additional or non-default metadata fields, for example a
592
+ # `userinfo_endpoint` (which Doorkeeper itself leaves null) when pairing with
593
+ # an OpenID Connect extension.
594
+ #
595
+ # The merge happens last, so it can also override computed fields - including
596
+ # `authorization_response_iss_parameter_supported`, which Doorkeeper derives
597
+ # from whether `issuer` is set. Overriding a computed field is allowed but is
598
+ # your responsibility to keep consistent with the server's actual behaviour
599
+ # (e.g. don't advertise iss support as true if no `issuer` is configured).
600
+ #
601
+ # custom_metadata(
602
+ # userinfo_endpoint: "https://auth.example.com/oauth/userinfo",
603
+ # )
544
604
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doorkeeper
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.9.3
4
+ version: 6.0.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felipe Elias Philipp
@@ -183,6 +183,7 @@ files:
183
183
  - app/controllers/doorkeeper/applications_controller.rb
184
184
  - app/controllers/doorkeeper/authorizations_controller.rb
185
185
  - app/controllers/doorkeeper/authorized_applications_controller.rb
186
+ - app/controllers/doorkeeper/metadata_controller.rb
186
187
  - app/controllers/doorkeeper/token_info_controller.rb
187
188
  - app/controllers/doorkeeper/tokens_controller.rb
188
189
  - app/helpers/doorkeeper/dashboard_helper.rb
@@ -202,6 +203,12 @@ files:
202
203
  - app/views/layouts/doorkeeper/application.html.erb
203
204
  - config/locales/en.yml
204
205
  - lib/doorkeeper.rb
206
+ - lib/doorkeeper/client_authentication.rb
207
+ - lib/doorkeeper/client_authentication/credentials.rb
208
+ - lib/doorkeeper/client_authentication/fallback_method.rb
209
+ - lib/doorkeeper/client_authentication/legacy_callable.rb
210
+ - lib/doorkeeper/client_authentication/method.rb
211
+ - lib/doorkeeper/client_authentication/registry.rb
205
212
  - lib/doorkeeper/config.rb
206
213
  - lib/doorkeeper/config/abstract_builder.rb
207
214
  - lib/doorkeeper/config/option.rb
@@ -239,7 +246,9 @@ files:
239
246
  - lib/doorkeeper/oauth/base_request.rb
240
247
  - lib/doorkeeper/oauth/base_response.rb
241
248
  - lib/doorkeeper/oauth/client.rb
242
- - lib/doorkeeper/oauth/client/credentials.rb
249
+ - lib/doorkeeper/oauth/client_authentication/client_secret_basic.rb
250
+ - lib/doorkeeper/oauth/client_authentication/client_secret_post.rb
251
+ - lib/doorkeeper/oauth/client_authentication/none.rb
243
252
  - lib/doorkeeper/oauth/client_credentials/creator.rb
244
253
  - lib/doorkeeper/oauth/client_credentials/issuer.rb
245
254
  - lib/doorkeeper/oauth/client_credentials/validator.rb
@@ -255,6 +264,7 @@ files:
255
264
  - lib/doorkeeper/oauth/hooks/context.rb
256
265
  - lib/doorkeeper/oauth/invalid_request_response.rb
257
266
  - lib/doorkeeper/oauth/invalid_token_response.rb
267
+ - lib/doorkeeper/oauth/metadata_response.rb
258
268
  - lib/doorkeeper/oauth/nonstandard.rb
259
269
  - lib/doorkeeper/oauth/password_access_token_request.rb
260
270
  - lib/doorkeeper/oauth/pre_authorization.rb