doorkeeper 5.9.5 → 6.0.0.beta2

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +70 -3
  3. data/README.md +204 -0
  4. data/app/controllers/doorkeeper/applications_controller.rb +38 -9
  5. data/app/controllers/doorkeeper/authorizations_controller.rb +17 -3
  6. data/app/controllers/doorkeeper/metadata_controller.rb +20 -0
  7. data/app/controllers/doorkeeper/tokens_controller.rb +15 -2
  8. data/app/views/doorkeeper/authorizations/new.html.erb +24 -0
  9. data/config/locales/en.yml +2 -0
  10. data/lib/doorkeeper/client_authentication/credentials.rb +11 -0
  11. data/lib/doorkeeper/client_authentication/fallback_method.rb +19 -0
  12. data/lib/doorkeeper/client_authentication/legacy_callable.rb +52 -0
  13. data/lib/doorkeeper/client_authentication/method.rb +40 -0
  14. data/lib/doorkeeper/client_authentication/registry.rb +47 -0
  15. data/lib/doorkeeper/client_authentication/verified_credentials.rb +19 -0
  16. data/lib/doorkeeper/client_authentication.rb +102 -0
  17. data/lib/doorkeeper/config/option.rb +1 -1
  18. data/lib/doorkeeper/config/validations.rb +162 -1
  19. data/lib/doorkeeper/config.rb +161 -9
  20. data/lib/doorkeeper/document_cache.rb +81 -0
  21. data/lib/doorkeeper/errors.rb +49 -0
  22. data/lib/doorkeeper/http_fetcher.rb +232 -0
  23. data/lib/doorkeeper/models/access_grant_mixin.rb +16 -0
  24. data/lib/doorkeeper/models/access_token_mixin.rb +81 -5
  25. data/lib/doorkeeper/models/application_mixin.rb +14 -6
  26. data/lib/doorkeeper/models/concerns/secret_storable.rb +10 -1
  27. data/lib/doorkeeper/oauth/authorization/code.rb +10 -0
  28. data/lib/doorkeeper/oauth/authorization/token.rb +13 -2
  29. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +11 -0
  30. data/lib/doorkeeper/oauth/authorization_code_request.rb +127 -3
  31. data/lib/doorkeeper/oauth/base_request.rb +1 -2
  32. data/lib/doorkeeper/oauth/client.rb +24 -0
  33. data/lib/doorkeeper/oauth/client_authentication/client_secret_basic.rb +57 -0
  34. data/lib/doorkeeper/oauth/client_authentication/client_secret_post.rb +33 -0
  35. data/lib/doorkeeper/oauth/client_authentication/none.rb +71 -0
  36. data/lib/doorkeeper/oauth/client_authentication/private_key_jwt/key_resolver.rb +104 -0
  37. data/lib/doorkeeper/oauth/client_authentication/private_key_jwt/replay_guard.rb +78 -0
  38. data/lib/doorkeeper/oauth/client_authentication/private_key_jwt.rb +247 -0
  39. data/lib/doorkeeper/oauth/client_credentials/creator.rb +25 -9
  40. data/lib/doorkeeper/oauth/client_credentials_request.rb +36 -5
  41. data/lib/doorkeeper/oauth/code_response.rb +16 -2
  42. data/lib/doorkeeper/oauth/error_response.rb +11 -2
  43. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +35 -23
  44. data/lib/doorkeeper/oauth/metadata_response.rb +165 -0
  45. data/lib/doorkeeper/oauth/password_access_token_request.rb +27 -1
  46. data/lib/doorkeeper/oauth/pre_authorization.rb +63 -8
  47. data/lib/doorkeeper/oauth/refresh_token_request.rb +51 -1
  48. data/lib/doorkeeper/oauth/resource_indicator_validator.rb +70 -0
  49. data/lib/doorkeeper/oauth/scopes.rb +25 -0
  50. data/lib/doorkeeper/oauth/token_introspection.rb +33 -7
  51. data/lib/doorkeeper/orm/active_record/mixins/application.rb +2 -2
  52. data/lib/doorkeeper/rails/routes/mapping.rb +1 -0
  53. data/lib/doorkeeper/rails/routes.rb +6 -0
  54. data/lib/doorkeeper/request.rb +59 -0
  55. data/lib/doorkeeper/server.rb +5 -2
  56. data/lib/doorkeeper/version.rb +4 -4
  57. data/lib/doorkeeper.rb +12 -4
  58. data/lib/generators/doorkeeper/grant_reuse_revocation_generator.rb +43 -0
  59. data/lib/generators/doorkeeper/resource_indicators_generator.rb +33 -0
  60. data/lib/generators/doorkeeper/templates/add_access_token_to_access_grants.rb.erb +10 -0
  61. data/lib/generators/doorkeeper/templates/enable_resource_indicators_migration.rb.erb +8 -0
  62. data/lib/generators/doorkeeper/templates/initializer.rb +131 -6
  63. data/lib/generators/doorkeeper/templates/migration.rb.erb +13 -0
  64. metadata +52 -4
  65. data/lib/doorkeeper/oauth/client/credentials.rb +0 -71
  66. data/lib/doorkeeper/oauth/stateless_token.rb +0 -139
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.5
4
+ version: 6.0.0.beta2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felipe Elias Philipp
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2026-08-07 00:00:00.000000000 Z
14
+ date: 2026-08-10 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: railties
@@ -125,6 +125,20 @@ dependencies:
125
125
  - - ">="
126
126
  - !ruby/object:Gem::Version
127
127
  version: '0'
128
+ - !ruby/object:Gem::Dependency
129
+ name: jwt
130
+ requirement: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '2.7'
135
+ type: :development
136
+ prerelease: false
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '2.7'
128
142
  - !ruby/object:Gem::Dependency
129
143
  name: rake
130
144
  requirement: !ruby/object:Gem::Requirement
@@ -167,6 +181,20 @@ dependencies:
167
181
  - - ">="
168
182
  - !ruby/object:Gem::Version
169
183
  version: '0'
184
+ - !ruby/object:Gem::Dependency
185
+ name: webmock
186
+ requirement: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - ">="
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
191
+ type: :development
192
+ prerelease: false
193
+ version_requirements: !ruby/object:Gem::Requirement
194
+ requirements:
195
+ - - ">="
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
170
198
  description: Doorkeeper is an OAuth 2 provider for Rails and Grape.
171
199
  email:
172
200
  - bulaj.nikita@gmail.com
@@ -184,6 +212,7 @@ files:
184
212
  - app/controllers/doorkeeper/applications_controller.rb
185
213
  - app/controllers/doorkeeper/authorizations_controller.rb
186
214
  - app/controllers/doorkeeper/authorized_applications_controller.rb
215
+ - app/controllers/doorkeeper/metadata_controller.rb
187
216
  - app/controllers/doorkeeper/token_info_controller.rb
188
217
  - app/controllers/doorkeeper/tokens_controller.rb
189
218
  - app/helpers/doorkeeper/dashboard_helper.rb
@@ -203,10 +232,18 @@ files:
203
232
  - app/views/layouts/doorkeeper/application.html.erb
204
233
  - config/locales/en.yml
205
234
  - lib/doorkeeper.rb
235
+ - lib/doorkeeper/client_authentication.rb
236
+ - lib/doorkeeper/client_authentication/credentials.rb
237
+ - lib/doorkeeper/client_authentication/fallback_method.rb
238
+ - lib/doorkeeper/client_authentication/legacy_callable.rb
239
+ - lib/doorkeeper/client_authentication/method.rb
240
+ - lib/doorkeeper/client_authentication/registry.rb
241
+ - lib/doorkeeper/client_authentication/verified_credentials.rb
206
242
  - lib/doorkeeper/config.rb
207
243
  - lib/doorkeeper/config/abstract_builder.rb
208
244
  - lib/doorkeeper/config/option.rb
209
245
  - lib/doorkeeper/config/validations.rb
246
+ - lib/doorkeeper/document_cache.rb
210
247
  - lib/doorkeeper/engine.rb
211
248
  - lib/doorkeeper/errors.rb
212
249
  - lib/doorkeeper/grant_flow.rb
@@ -216,6 +253,7 @@ files:
216
253
  - lib/doorkeeper/grape/authorization_decorator.rb
217
254
  - lib/doorkeeper/grape/helpers.rb
218
255
  - lib/doorkeeper/helpers/controller.rb
256
+ - lib/doorkeeper/http_fetcher.rb
219
257
  - lib/doorkeeper/models/access_grant_mixin.rb
220
258
  - lib/doorkeeper/models/access_token_mixin.rb
221
259
  - lib/doorkeeper/models/application_mixin.rb
@@ -240,7 +278,12 @@ files:
240
278
  - lib/doorkeeper/oauth/base_request.rb
241
279
  - lib/doorkeeper/oauth/base_response.rb
242
280
  - lib/doorkeeper/oauth/client.rb
243
- - lib/doorkeeper/oauth/client/credentials.rb
281
+ - lib/doorkeeper/oauth/client_authentication/client_secret_basic.rb
282
+ - lib/doorkeeper/oauth/client_authentication/client_secret_post.rb
283
+ - lib/doorkeeper/oauth/client_authentication/none.rb
284
+ - lib/doorkeeper/oauth/client_authentication/private_key_jwt.rb
285
+ - lib/doorkeeper/oauth/client_authentication/private_key_jwt/key_resolver.rb
286
+ - lib/doorkeeper/oauth/client_authentication/private_key_jwt/replay_guard.rb
244
287
  - lib/doorkeeper/oauth/client_credentials/creator.rb
245
288
  - lib/doorkeeper/oauth/client_credentials/issuer.rb
246
289
  - lib/doorkeeper/oauth/client_credentials/validator.rb
@@ -256,12 +299,13 @@ files:
256
299
  - lib/doorkeeper/oauth/hooks/context.rb
257
300
  - lib/doorkeeper/oauth/invalid_request_response.rb
258
301
  - lib/doorkeeper/oauth/invalid_token_response.rb
302
+ - lib/doorkeeper/oauth/metadata_response.rb
259
303
  - lib/doorkeeper/oauth/nonstandard.rb
260
304
  - lib/doorkeeper/oauth/password_access_token_request.rb
261
305
  - lib/doorkeeper/oauth/pre_authorization.rb
262
306
  - lib/doorkeeper/oauth/refresh_token_request.rb
307
+ - lib/doorkeeper/oauth/resource_indicator_validator.rb
263
308
  - lib/doorkeeper/oauth/scopes.rb
264
- - lib/doorkeeper/oauth/stateless_token.rb
265
309
  - lib/doorkeeper/oauth/token.rb
266
310
  - lib/doorkeeper/oauth/token_introspection.rb
267
311
  - lib/doorkeeper/oauth/token_request.rb
@@ -305,17 +349,21 @@ files:
305
349
  - lib/generators/doorkeeper/application_owner_generator.rb
306
350
  - lib/generators/doorkeeper/confidential_applications_generator.rb
307
351
  - lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb
352
+ - lib/generators/doorkeeper/grant_reuse_revocation_generator.rb
308
353
  - lib/generators/doorkeeper/install_generator.rb
309
354
  - lib/generators/doorkeeper/migration_generator.rb
310
355
  - lib/generators/doorkeeper/pkce_generator.rb
311
356
  - lib/generators/doorkeeper/previous_refresh_token_generator.rb
312
357
  - lib/generators/doorkeeper/remove_applications_secret_not_null_constraint_generator.rb
358
+ - lib/generators/doorkeeper/resource_indicators_generator.rb
313
359
  - lib/generators/doorkeeper/templates/README
360
+ - lib/generators/doorkeeper/templates/add_access_token_to_access_grants.rb.erb
314
361
  - lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb
315
362
  - lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb
316
363
  - lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb
317
364
  - lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb
318
365
  - lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb
366
+ - lib/generators/doorkeeper/templates/enable_resource_indicators_migration.rb.erb
319
367
  - lib/generators/doorkeeper/templates/initializer.rb
320
368
  - lib/generators/doorkeeper/templates/migration.rb.erb
321
369
  - lib/generators/doorkeeper/templates/remove_applications_secret_not_null_constraint.rb.erb
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Doorkeeper
4
- module OAuth
5
- class Client
6
- Credentials = Struct.new(:uid, :secret) do
7
- class << self
8
- # Extracts the client credentials from the request using the
9
- # configured extraction methods. The first method that yields
10
- # credentials wins, as it always has.
11
- #
12
- # Raises Errors::MultipleClientAuthMethods when the request
13
- # authenticates the client with more than one method, which RFC 6749
14
- # §2.3 forbids ("The client MUST NOT use more than one authentication
15
- # method in each request").
16
- def from_request(request, *credentials_methods)
17
- # Callable extractors are opaque: they may legitimately overlap the
18
- # built-in methods they are configured with, and evaluating all of
19
- # them would also break the existing contract that the extractors
20
- # after the matching one are not called. Such configurations keep
21
- # the historical behaviour untouched.
22
- return first_from_request(request, credentials_methods) unless credentials_methods.all?(Symbol)
23
-
24
- credentials = credentials_methods.filter_map { |method| extract(request, method) }
25
-
26
- # Only credentials carrying a secret authenticate the client. A bare
27
- # uid is a public client identifying itself (RFC 6749 §2.3 "none"),
28
- # not an authentication method of its own, so it doesn't count
29
- # towards the single-method rule — RFC 7521 §4.2, for example,
30
- # explicitly allows a client_id next to another authentication
31
- # method.
32
- raise Errors::MultipleClientAuthMethods if credentials.count { |c| c.secret.present? } > 1
33
-
34
- credentials.first
35
- end
36
-
37
- def from_params(request)
38
- request.parameters.values_at(:client_id, :client_secret)
39
- end
40
-
41
- def from_basic(request)
42
- authorization = request.authorization
43
- if authorization.present? && authorization =~ /^Basic (.*)/im
44
- Base64.decode64(Regexp.last_match(1)).split(/:/, 2)
45
- end
46
- end
47
-
48
- private
49
-
50
- def first_from_request(request, credentials_methods)
51
- credentials_methods.inject(nil) do |_, method|
52
- credentials = extract(request, method)
53
- break credentials if credentials
54
- end
55
- end
56
-
57
- # Returns the credentials the given method extracts from the request,
58
- # or nil when it extracts none.
59
- def extract(request, method)
60
- method = self.method(method) if method.is_a?(Symbol)
61
- Credentials.new(*method.call(request)).presence
62
- end
63
- end
64
-
65
- # Public clients may have their secret blank, but "credentials" are
66
- # still present
67
- delegate :blank?, to: :uid
68
- end
69
- end
70
- end
71
- end
@@ -1,139 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Doorkeeper
4
- module OAuth
5
- # In-memory representation of a verified JWT access token. Returned by
6
- # OAuth::Token.authenticate when stateless_jwt_tokens is enabled, so that
7
- # the existing doorkeeper_token / doorkeeper_authorize! helpers work
8
- # without any database read.
9
- #
10
- # Stateless tokens cannot be revoked or refreshed server-side: #revoked?
11
- # is always false and #revoke is a no-op. Rely on short token lifetimes
12
- # (access_token_expires_in) for early invalidation.
13
- class StatelessToken
14
- attr_reader :claims, :application, :raw_token
15
-
16
- def initialize(claims:, application: nil, raw_token: nil)
17
- @claims = claims
18
- @application = application
19
- @raw_token = raw_token
20
- end
21
-
22
- def token_type
23
- "Bearer"
24
- end
25
-
26
- # The raw token string. Used by introspection's
27
- # authorized_token_matches_introspected? security check.
28
- def token
29
- @raw_token
30
- end
31
-
32
- # Issuance path: the freshly-signed JWT returned to the client.
33
- def plaintext_token
34
- @raw_token
35
- end
36
-
37
- def plaintext_refresh_token
38
- nil
39
- end
40
-
41
- def resource_owner_id
42
- claims["resource_owner_id"] || claims["sub"]
43
- end
44
-
45
- def application_id
46
- application&.id
47
- end
48
-
49
- def scopes_string
50
- claims["scope"].to_s
51
- end
52
-
53
- def scopes
54
- OAuth::Scopes.from_string(scopes_string)
55
- end
56
-
57
- def includes_scope?(*required_scopes)
58
- required_scopes.blank? || required_scopes.any? { |scope| scopes.exists?(scope.to_s) }
59
- end
60
-
61
- def custom_attributes
62
- claims.slice(*Doorkeeper.config.custom_access_token_attributes.map(&:to_s))
63
- end
64
-
65
- # RFC 8707 resource indicator. Stored as a space-delimited string to
66
- # match the AccessToken column shape consumed by token_introspection.
67
- def resource
68
- aud = claims["aud"]
69
- return nil if aud.blank?
70
-
71
- aud.is_a?(Array) ? aud.join(" ") : aud.to_s
72
- end
73
-
74
- def created_at
75
- Time.at(claims["iat"].to_i).utc
76
- end
77
-
78
- def expires_in
79
- claims["expires_in"]
80
- end
81
-
82
- def expires_at
83
- return nil unless claims["exp"]
84
-
85
- Time.at(claims["exp"].to_i).utc
86
- end
87
-
88
- def expires_in_seconds
89
- return nil unless expires_at
90
-
91
- expires = expires_at - Time.now.utc
92
- sec = expires.seconds.round(0)
93
- sec > 0 ? sec : 0
94
- end
95
-
96
- def expired?
97
- return false unless expires_at
98
-
99
- Time.now.utc > expires_at
100
- end
101
-
102
- def revoked?
103
- false
104
- end
105
-
106
- def accessible?
107
- !expired? && !revoked?
108
- end
109
-
110
- def acceptable?(required_scopes)
111
- accessible? && includes_scope?(*required_scopes)
112
- end
113
-
114
- def revoke_previous_refresh_token!
115
- # no-op: stateless tokens carry no refresh-token chain to revoke.
116
- end
117
-
118
- def revoke
119
- # no-op: stateless tokens cannot be revoked server-side.
120
- end
121
-
122
- def revocable?
123
- false
124
- end
125
-
126
- def as_json(_options = {})
127
- {
128
- resource_owner_id: resource_owner_id,
129
- scope: scopes,
130
- expires_in: expires_in_seconds,
131
- application: { uid: application.try(:uid) },
132
- created_at: created_at.to_i,
133
- }.tap do |json|
134
- json[:resource_owner_type] = claims["resource_owner_type"] if Doorkeeper.configuration.polymorphic_resource_owner?
135
- end
136
- end
137
- end
138
- end
139
- end