doorkeeper 4.4.3 → 5.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of doorkeeper might be problematic. Click here for more details.

Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +2 -0
  4. data/Appraisals +2 -2
  5. data/Gemfile +1 -1
  6. data/NEWS.md +36 -17
  7. data/README.md +85 -3
  8. data/Rakefile +6 -0
  9. data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
  10. data/app/controllers/doorkeeper/application_controller.rb +4 -3
  11. data/app/controllers/doorkeeper/application_metal_controller.rb +4 -0
  12. data/app/controllers/doorkeeper/applications_controller.rb +42 -22
  13. data/app/controllers/doorkeeper/authorizations_controller.rb +55 -12
  14. data/app/controllers/doorkeeper/authorized_applications_controller.rb +15 -1
  15. data/app/controllers/doorkeeper/tokens_controller.rb +12 -15
  16. data/app/helpers/doorkeeper/dashboard_helper.rb +7 -7
  17. data/app/validators/redirect_uri_validator.rb +3 -2
  18. data/app/views/doorkeeper/applications/_delete_form.html.erb +3 -1
  19. data/app/views/doorkeeper/applications/_form.html.erb +25 -24
  20. data/app/views/doorkeeper/applications/edit.html.erb +1 -1
  21. data/app/views/doorkeeper/applications/index.html.erb +17 -7
  22. data/app/views/doorkeeper/applications/new.html.erb +1 -1
  23. data/app/views/doorkeeper/applications/show.html.erb +6 -6
  24. data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
  25. data/app/views/doorkeeper/authorizations/new.html.erb +4 -0
  26. data/app/views/layouts/doorkeeper/admin.html.erb +15 -15
  27. data/config/locales/en.yml +9 -1
  28. data/doorkeeper.gemspec +0 -2
  29. data/gemfiles/rails_5_2.gemfile +1 -1
  30. data/lib/doorkeeper/config.rb +58 -35
  31. data/lib/doorkeeper/engine.rb +4 -0
  32. data/lib/doorkeeper/errors.rb +2 -5
  33. data/lib/doorkeeper/grape/helpers.rb +1 -1
  34. data/lib/doorkeeper/helpers/controller.rb +7 -2
  35. data/lib/doorkeeper/models/access_grant_mixin.rb +56 -0
  36. data/lib/doorkeeper/models/access_token_mixin.rb +38 -21
  37. data/lib/doorkeeper/models/concerns/scopes.rb +1 -1
  38. data/lib/doorkeeper/oauth/authorization/code.rb +31 -8
  39. data/lib/doorkeeper/oauth/authorization/context.rb +15 -0
  40. data/lib/doorkeeper/oauth/authorization/token.rb +23 -6
  41. data/lib/doorkeeper/oauth/authorization_code_request.rb +27 -2
  42. data/lib/doorkeeper/oauth/base_request.rb +18 -8
  43. data/lib/doorkeeper/oauth/client/credentials.rb +1 -1
  44. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +6 -1
  45. data/lib/doorkeeper/oauth/client_credentials/validation.rb +4 -2
  46. data/lib/doorkeeper/oauth/error_response.rb +11 -3
  47. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +0 -8
  48. data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -4
  49. data/lib/doorkeeper/oauth/pre_authorization.rb +41 -11
  50. data/lib/doorkeeper/oauth/refresh_token_request.rb +6 -1
  51. data/lib/doorkeeper/oauth/scopes.rb +1 -1
  52. data/lib/doorkeeper/oauth/token.rb +5 -2
  53. data/lib/doorkeeper/oauth/token_introspection.rb +2 -2
  54. data/lib/doorkeeper/oauth/token_response.rb +4 -2
  55. data/lib/doorkeeper/oauth.rb +13 -0
  56. data/lib/doorkeeper/orm/active_record/application.rb +13 -16
  57. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +26 -0
  58. data/lib/doorkeeper/orm/active_record.rb +2 -0
  59. data/lib/doorkeeper/rails/helpers.rb +2 -4
  60. data/lib/doorkeeper/rails/routes.rb +14 -6
  61. data/lib/doorkeeper/rake/db.rake +40 -0
  62. data/lib/doorkeeper/rake/setup.rake +6 -0
  63. data/lib/doorkeeper/rake.rb +14 -0
  64. data/lib/doorkeeper/request.rb +28 -28
  65. data/lib/doorkeeper/version.rb +5 -25
  66. data/lib/doorkeeper.rb +4 -17
  67. data/lib/generators/doorkeeper/application_owner_generator.rb +23 -18
  68. data/lib/generators/doorkeeper/confidential_applications_generator.rb +32 -0
  69. data/lib/generators/doorkeeper/install_generator.rb +17 -9
  70. data/lib/generators/doorkeeper/migration_generator.rb +23 -18
  71. data/lib/generators/doorkeeper/pkce_generator.rb +32 -0
  72. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +29 -24
  73. data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
  74. data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +6 -0
  75. data/lib/generators/doorkeeper/templates/initializer.rb +60 -9
  76. data/lib/generators/doorkeeper/views_generator.rb +3 -1
  77. data/spec/controllers/application_metal_controller_spec.rb +50 -0
  78. data/spec/controllers/applications_controller_spec.rb +126 -13
  79. data/spec/controllers/authorizations_controller_spec.rb +252 -49
  80. data/spec/controllers/protected_resources_controller_spec.rb +16 -16
  81. data/spec/controllers/token_info_controller_spec.rb +4 -12
  82. data/spec/controllers/tokens_controller_spec.rb +19 -73
  83. data/spec/dummy/app/assets/config/manifest.js +2 -0
  84. data/spec/dummy/config/environments/test.rb +4 -5
  85. data/spec/dummy/config/initializers/doorkeeper.rb +5 -4
  86. data/spec/dummy/config/initializers/new_framework_defaults.rb +4 -0
  87. data/spec/dummy/config/routes.rb +3 -42
  88. data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +6 -0
  89. data/spec/dummy/db/migrate/{20180210183654_add_confidential_to_application.rb → 20180210183654_add_confidential_to_applications.rb} +1 -1
  90. data/spec/dummy/db/schema.rb +36 -36
  91. data/spec/generators/application_owner_generator_spec.rb +1 -1
  92. data/spec/generators/confidential_applications_generator_spec.rb +45 -0
  93. data/spec/generators/install_generator_spec.rb +1 -1
  94. data/spec/generators/migration_generator_spec.rb +1 -1
  95. data/spec/generators/pkce_generator_spec.rb +43 -0
  96. data/spec/generators/previous_refresh_token_generator_spec.rb +1 -1
  97. data/spec/generators/views_generator_spec.rb +1 -1
  98. data/spec/grape/grape_integration_spec.rb +1 -1
  99. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
  100. data/spec/lib/config_spec.rb +51 -31
  101. data/spec/lib/doorkeeper_spec.rb +1 -126
  102. data/spec/lib/models/expirable_spec.rb +0 -3
  103. data/spec/lib/models/revocable_spec.rb +0 -2
  104. data/spec/lib/models/scopes_spec.rb +0 -4
  105. data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -4
  106. data/spec/lib/oauth/authorization_code_request_spec.rb +9 -2
  107. data/spec/lib/oauth/base_request_spec.rb +16 -2
  108. data/spec/lib/oauth/base_response_spec.rb +1 -1
  109. data/spec/lib/oauth/client/credentials_spec.rb +1 -3
  110. data/spec/lib/oauth/client_credentials/creator_spec.rb +5 -1
  111. data/spec/lib/oauth/client_credentials/issuer_spec.rb +26 -7
  112. data/spec/lib/oauth/client_credentials/validation_spec.rb +2 -3
  113. data/spec/lib/oauth/client_credentials_integration_spec.rb +1 -1
  114. data/spec/lib/oauth/client_credentials_request_spec.rb +3 -5
  115. data/spec/lib/oauth/client_spec.rb +0 -3
  116. data/spec/lib/oauth/code_request_spec.rb +4 -2
  117. data/spec/lib/oauth/error_response_spec.rb +0 -3
  118. data/spec/lib/oauth/error_spec.rb +0 -2
  119. data/spec/lib/oauth/forbidden_token_response_spec.rb +1 -4
  120. data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -3
  121. data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -1
  122. data/spec/lib/oauth/helpers/uri_checker_spec.rb +5 -7
  123. data/spec/lib/oauth/invalid_token_response_spec.rb +1 -4
  124. data/spec/lib/oauth/password_access_token_request_spec.rb +37 -2
  125. data/spec/lib/oauth/pre_authorization_spec.rb +33 -4
  126. data/spec/lib/oauth/refresh_token_request_spec.rb +11 -7
  127. data/spec/lib/oauth/scopes_spec.rb +0 -3
  128. data/spec/lib/oauth/token_request_spec.rb +4 -5
  129. data/spec/lib/oauth/token_response_spec.rb +0 -1
  130. data/spec/lib/oauth/token_spec.rb +37 -14
  131. data/spec/lib/orm/active_record/stale_records_cleaner_spec.rb +79 -0
  132. data/spec/lib/request/strategy_spec.rb +0 -1
  133. data/spec/lib/server_spec.rb +1 -1
  134. data/spec/models/doorkeeper/access_grant_spec.rb +1 -1
  135. data/spec/models/doorkeeper/access_token_spec.rb +50 -16
  136. data/spec/models/doorkeeper/application_spec.rb +1 -47
  137. data/spec/requests/applications/applications_request_spec.rb +89 -1
  138. data/spec/requests/applications/authorized_applications_spec.rb +1 -1
  139. data/spec/requests/endpoints/authorization_spec.rb +1 -1
  140. data/spec/requests/endpoints/token_spec.rb +7 -5
  141. data/spec/requests/flows/authorization_code_errors_spec.rb +1 -1
  142. data/spec/requests/flows/authorization_code_spec.rb +198 -2
  143. data/spec/requests/flows/client_credentials_spec.rb +46 -6
  144. data/spec/requests/flows/implicit_grant_errors_spec.rb +1 -1
  145. data/spec/requests/flows/implicit_grant_spec.rb +38 -11
  146. data/spec/requests/flows/password_spec.rb +56 -2
  147. data/spec/requests/flows/refresh_token_spec.rb +2 -2
  148. data/spec/requests/flows/revoke_token_spec.rb +11 -11
  149. data/spec/requests/flows/skip_authorization_spec.rb +16 -11
  150. data/spec/requests/protected_resources/metal_spec.rb +1 -1
  151. data/spec/requests/protected_resources/private_api_spec.rb +1 -1
  152. data/spec/routing/custom_controller_routes_spec.rb +59 -7
  153. data/spec/routing/default_routes_spec.rb +2 -2
  154. data/spec/routing/scoped_routes_spec.rb +16 -2
  155. data/spec/spec_helper.rb +54 -3
  156. data/spec/spec_helper_integration.rb +2 -74
  157. data/spec/support/dependencies/{factory_girl.rb → factory_bot.rb} +0 -0
  158. data/spec/support/doorkeeper_rspec.rb +19 -0
  159. data/spec/support/helpers/authorization_request_helper.rb +4 -4
  160. data/spec/support/helpers/request_spec_helper.rb +2 -2
  161. data/spec/support/helpers/url_helper.rb +7 -3
  162. data/spec/support/http_method_shim.rb +12 -16
  163. data/spec/validators/redirect_uri_validator_spec.rb +7 -1
  164. data/spec/version/version_spec.rb +3 -3
  165. data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
  166. metadata +33 -31
  167. data/lib/generators/doorkeeper/add_client_confidentiality_generator.rb +0 -31
  168. data/lib/generators/doorkeeper/templates/add_confidential_to_application_migration.rb.erb +0 -11
  169. data/spec/controllers/application_metal_controller.rb +0 -10
@@ -70,30 +70,34 @@ module Doorkeeper
70
70
  else
71
71
  resource_owner_or_id
72
72
  end
73
- token = last_authorized_token_for(application.try(:id), resource_owner_id)
74
- if token && scopes_match?(token.scopes, scopes, application.try(:scopes))
75
- token
73
+
74
+ tokens = authorized_tokens_for(application.try(:id), resource_owner_id)
75
+ tokens.detect do |token|
76
+ scopes_match?(token.scopes, scopes, application.try(:scopes))
76
77
  end
77
78
  end
78
79
 
79
- # Checks whether the token scopes match the scopes from the parameters or
80
- # Application scopes (if present).
80
+ # Checks whether the token scopes match the scopes from the parameters
81
81
  #
82
82
  # @param token_scopes [#to_s]
83
83
  # set of scopes (any object that responds to `#to_s`)
84
- # @param param_scopes [String]
84
+ # @param param_scopes [Doorkeeper::OAuth::Scopes]
85
85
  # scopes from params
86
- # @param app_scopes [String]
86
+ # @param app_scopes [Doorkeeper::OAuth::Scopes]
87
87
  # Application scopes
88
88
  #
89
- # @return [Boolean] true if all scopes are blank or matches
89
+ # @return [Boolean] true if the param scopes match the token scopes,
90
+ # and all the param scopes are defined in the application (or in the
91
+ # server configuration if the application doesn't define any scopes),
90
92
  # and false in other cases
91
93
  #
92
94
  def scopes_match?(token_scopes, param_scopes, app_scopes)
93
- (!token_scopes.present? && !param_scopes.present?) ||
94
- Doorkeeper::OAuth::Helpers::ScopeChecker.match?(
95
- token_scopes.to_s,
96
- param_scopes,
95
+ return true if token_scopes.empty? && param_scopes.empty?
96
+
97
+ (token_scopes.sort == param_scopes.sort) &&
98
+ Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
99
+ param_scopes.to_s,
100
+ Doorkeeper.configuration.scopes,
97
101
  app_scopes
98
102
  )
99
103
  end
@@ -118,9 +122,8 @@ module Doorkeeper
118
122
  def find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token)
119
123
  if Doorkeeper.configuration.reuse_access_token
120
124
  access_token = matching_token_for(application, resource_owner_id, scopes)
121
- if access_token && !access_token.expired?
122
- return access_token
123
- end
125
+
126
+ return access_token if access_token && !access_token.expired?
124
127
  end
125
128
 
126
129
  create!(
@@ -132,7 +135,7 @@ module Doorkeeper
132
135
  )
133
136
  end
134
137
 
135
- # Looking for not revoked Access Token record that belongs to specific
138
+ # Looking for not revoked Access Token records that belongs to specific
136
139
  # Application and Resource Owner.
137
140
  #
138
141
  # @param application_id [Integer]
@@ -140,14 +143,28 @@ module Doorkeeper
140
143
  # @param resource_owner_id [Integer]
141
144
  # ID of the Resource Owner model instance
142
145
  #
146
+ # @return [Doorkeeper::AccessToken] array of matching AccessToken objects
147
+ #
148
+ def authorized_tokens_for(application_id, resource_owner_id)
149
+ ordered_by(:created_at, :desc)
150
+ .where(application_id: application_id,
151
+ resource_owner_id: resource_owner_id,
152
+ revoked_at: nil)
153
+ end
154
+
155
+ # Convenience method for backwards-compatibility, return the last
156
+ # matching token for the given Application and Resource Owner.
157
+ #
158
+ # @param application_id [Integer]
159
+ # ID of the Application model instance
160
+ # @param resource_owner_id [Integer]
161
+ # ID of the Resource Owner model instance
162
+ #
143
163
  # @return [Doorkeeper::AccessToken, nil] matching AccessToken object or
144
164
  # nil if nothing was found
145
165
  #
146
166
  def last_authorized_token_for(application_id, resource_owner_id)
147
- ordered_by(:created_at, :desc).
148
- find_by(application_id: application_id,
149
- resource_owner_id: resource_owner_id,
150
- revoked_at: nil)
167
+ authorized_tokens_for(application_id, resource_owner_id).first
151
168
  end
152
169
  end
153
170
 
@@ -156,7 +173,7 @@ module Doorkeeper
156
173
  # The OAuth 2.0 Authorization Framework: Bearer Token Usage
157
174
  #
158
175
  def token_type
159
- 'Bearer'
176
+ 'bearer'
160
177
  end
161
178
 
162
179
  def use_refresh_token?
@@ -10,7 +10,7 @@ module Doorkeeper
10
10
  end
11
11
 
12
12
  def includes_scope?(*required_scopes)
13
- required_scopes.blank? || required_scopes.any? { |s| scopes.exists?(s.to_s) }
13
+ required_scopes.blank? || required_scopes.any? { |scope| scopes.exists?(scope.to_s) }
14
14
  end
15
15
  end
16
16
  end
@@ -5,18 +5,12 @@ module Doorkeeper
5
5
  attr_accessor :pre_auth, :resource_owner, :token
6
6
 
7
7
  def initialize(pre_auth, resource_owner)
8
- @pre_auth = pre_auth
8
+ @pre_auth = pre_auth
9
9
  @resource_owner = resource_owner
10
10
  end
11
11
 
12
12
  def issue_token
13
- @token ||= AccessGrant.create!(
14
- application_id: pre_auth.client.id,
15
- resource_owner_id: resource_owner.id,
16
- expires_in: configuration.authorization_code_expires_in,
17
- redirect_uri: pre_auth.redirect_uri,
18
- scopes: pre_auth.scopes.to_s
19
- )
13
+ @token ||= AccessGrant.create! access_grant_attributes
20
14
  end
21
15
 
22
16
  def native_redirect
@@ -26,6 +20,35 @@ module Doorkeeper
26
20
  def configuration
27
21
  Doorkeeper.configuration
28
22
  end
23
+
24
+ private
25
+
26
+ def authorization_code_expires_in
27
+ configuration.authorization_code_expires_in
28
+ end
29
+
30
+ def access_grant_attributes
31
+ pkce_attributes.merge application_id: pre_auth.client.id,
32
+ resource_owner_id: resource_owner.id,
33
+ expires_in: authorization_code_expires_in,
34
+ redirect_uri: pre_auth.redirect_uri,
35
+ scopes: pre_auth.scopes.to_s
36
+ end
37
+
38
+ def pkce_attributes
39
+ return {} unless pkce_supported?
40
+
41
+ {
42
+ code_challenge: pre_auth.code_challenge,
43
+ code_challenge_method: pre_auth.code_challenge_method
44
+ }
45
+ end
46
+
47
+ # ensures firstly, if migration with additional pcke columns was
48
+ # generated and migrated
49
+ def pkce_supported?
50
+ Doorkeeper::AccessGrant.pkce_supported?
51
+ end
29
52
  end
30
53
  end
31
54
  end
@@ -0,0 +1,15 @@
1
+ module Doorkeeper
2
+ module OAuth
3
+ module Authorization
4
+ class Context
5
+ attr_reader :client, :grant_type, :scopes
6
+
7
+ def initialize(client, grant_type, scopes)
8
+ @client = client
9
+ @grant_type = grant_type
10
+ @scopes = scopes
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -5,8 +5,8 @@ module Doorkeeper
5
5
  attr_accessor :pre_auth, :resource_owner, :token
6
6
 
7
7
  class << self
8
- def access_token_expires_in(server, pre_auth_or_oauth_client)
9
- if (expiration = custom_expiration(server, pre_auth_or_oauth_client))
8
+ def access_token_expires_in(server, pre_auth_or_oauth_client, grant_type, scopes)
9
+ if (expiration = custom_expiration(server, pre_auth_or_oauth_client, grant_type, scopes))
10
10
  expiration
11
11
  else
12
12
  server.access_token_expires_in
@@ -15,14 +15,19 @@ module Doorkeeper
15
15
 
16
16
  private
17
17
 
18
- def custom_expiration(server, pre_auth_or_oauth_client)
18
+ def custom_expiration(server, pre_auth_or_oauth_client, grant_type, scopes)
19
19
  oauth_client = if pre_auth_or_oauth_client.respond_to?(:client)
20
20
  pre_auth_or_oauth_client.client
21
21
  else
22
22
  pre_auth_or_oauth_client
23
23
  end
24
+ context = Doorkeeper::OAuth::Authorization::Context.new(
25
+ oauth_client,
26
+ grant_type,
27
+ scopes
28
+ )
24
29
 
25
- server.custom_access_token_expires_in.call(oauth_client)
30
+ server.custom_access_token_expires_in.call(context)
26
31
  end
27
32
  end
28
33
 
@@ -36,14 +41,19 @@ module Doorkeeper
36
41
  pre_auth.client,
37
42
  resource_owner.id,
38
43
  pre_auth.scopes,
39
- self.class.access_token_expires_in(configuration, pre_auth),
44
+ self.class.access_token_expires_in(
45
+ configuration,
46
+ pre_auth,
47
+ Doorkeeper::OAuth::IMPLICIT,
48
+ pre_auth.scopes
49
+ ),
40
50
  false
41
51
  )
42
52
  end
43
53
 
44
54
  def native_redirect
45
55
  {
46
- controller: 'doorkeeper/token_info',
56
+ controller: controller,
47
57
  action: :show,
48
58
  access_token: token.token
49
59
  }
@@ -54,6 +64,13 @@ module Doorkeeper
54
64
  def configuration
55
65
  Doorkeeper.configuration
56
66
  end
67
+
68
+ def controller
69
+ @controller ||= begin
70
+ mapping = Doorkeeper::Rails::Routes.mapping[:token_info] || {}
71
+ mapping[:controllers] || 'doorkeeper/token_info'
72
+ end
73
+ end
57
74
  end
58
75
  end
59
76
  end
@@ -6,18 +6,26 @@ module Doorkeeper
6
6
  validate :grant, error: :invalid_grant
7
7
  # @see https://tools.ietf.org/html/rfc6749#section-5.2
8
8
  validate :redirect_uri, error: :invalid_grant
9
+ validate :code_verifier, error: :invalid_grant
9
10
 
10
- attr_accessor :server, :grant, :client, :redirect_uri, :access_token
11
+ attr_accessor :server, :grant, :client, :redirect_uri, :access_token,
12
+ :code_verifier
11
13
 
12
14
  def initialize(server, grant, client, parameters = {})
13
15
  @server = server
14
16
  @client = client
15
17
  @grant = grant
18
+ @grant_type = Doorkeeper::OAuth::AUTHORIZATION_CODE
16
19
  @redirect_uri = parameters[:redirect_uri]
20
+ @code_verifier = parameters[:code_verifier]
17
21
  end
18
22
 
19
23
  private
20
24
 
25
+ def client_by_uid(parameters)
26
+ Doorkeeper::Application.by_uid(parameters[:client_id])
27
+ end
28
+
21
29
  def before_successful_response
22
30
  grant.transaction do
23
31
  grant.lock!
@@ -33,11 +41,13 @@ module Doorkeeper
33
41
  end
34
42
 
35
43
  def validate_attributes
44
+ return false if grant && grant.uses_pkce? && code_verifier.blank?
45
+ return false if grant && !grant.pkce_supported? && !code_verifier.blank?
36
46
  redirect_uri.present?
37
47
  end
38
48
 
39
49
  def validate_client
40
- !!client
50
+ !client.nil?
41
51
  end
42
52
 
43
53
  def validate_grant
@@ -51,6 +61,21 @@ module Doorkeeper
51
61
  grant.redirect_uri
52
62
  )
53
63
  end
64
+
65
+ # if either side (server or client) request pkce, check the verifier
66
+ # against the DB - if pkce is supported
67
+ def validate_code_verifier
68
+ return true unless grant.uses_pkce? || code_verifier
69
+ return false unless grant.pkce_supported?
70
+
71
+ if grant.code_challenge_method == 'S256'
72
+ grant.code_challenge == AccessGrant.generate_code_challenge(code_verifier)
73
+ elsif grant.code_challenge_method == 'plain'
74
+ grant.code_challenge == code_verifier
75
+ else
76
+ false
77
+ end
78
+ end
54
79
  end
55
80
  end
56
81
  end
@@ -3,6 +3,8 @@ module Doorkeeper
3
3
  class BaseRequest
4
4
  include Validations
5
5
 
6
+ attr_reader :grant_type
7
+
6
8
  def authorize
7
9
  validate
8
10
 
@@ -17,11 +19,7 @@ module Doorkeeper
17
19
  end
18
20
 
19
21
  def scopes
20
- @scopes ||= if @original_scopes.present?
21
- OAuth::Scopes.from_string(@original_scopes)
22
- else
23
- default_scopes
24
- end
22
+ @scopes ||= build_scopes
25
23
  end
26
24
 
27
25
  def default_scopes
@@ -37,7 +35,7 @@ module Doorkeeper
37
35
  client,
38
36
  resource_owner_id,
39
37
  scopes,
40
- Authorization::Token.access_token_expires_in(server, client),
38
+ Authorization::Token.access_token_expires_in(server, client, grant_type, scopes),
41
39
  server.refresh_token_enabled?
42
40
  )
43
41
  end
@@ -47,8 +45,20 @@ module Doorkeeper
47
45
  end
48
46
 
49
47
  def after_successful_response
50
- Doorkeeper.configuration.after_successful_strategy_response.
51
- call(self, @response)
48
+ Doorkeeper.configuration.after_successful_strategy_response.call(self, @response)
49
+ end
50
+
51
+ private
52
+
53
+ def build_scopes
54
+ if @original_scopes.present?
55
+ OAuth::Scopes.from_string(@original_scopes)
56
+ else
57
+ client_scopes = @client.try(:scopes)
58
+ return default_scopes if client_scopes.blank?
59
+
60
+ default_scopes & @client.scopes
61
+ end
52
62
  end
53
63
  end
54
64
  end
@@ -4,7 +4,7 @@ module Doorkeeper
4
4
  Credentials = Struct.new(:uid, :secret) do
5
5
  class << self
6
6
  def from_request(request, *credentials_methods)
7
- credentials_methods.inject(nil) do |credentials, method|
7
+ credentials_methods.inject(nil) do |_, method|
8
8
  method = self.method(method) if method.is_a?(Symbol)
9
9
  credentials = Credentials.new(*method.call(request))
10
10
  break credentials unless credentials.blank?
@@ -25,7 +25,12 @@ module Doorkeeper
25
25
  private
26
26
 
27
27
  def create_token(client, scopes, creator)
28
- ttl = Authorization::Token.access_token_expires_in(@server, client)
28
+ ttl = Authorization::Token.access_token_expires_in(
29
+ @server,
30
+ client,
31
+ Doorkeeper::OAuth::CLIENT_CREDENTIALS,
32
+ scopes
33
+ )
29
34
 
30
35
  creator.call(
31
36
  client,
@@ -13,7 +13,9 @@ module Doorkeeper
13
13
  validate :scopes, error: :invalid_scope
14
14
 
15
15
  def initialize(server, request)
16
- @server, @request, @client = server, request, request.client
16
+ @server = server
17
+ @request = request
18
+ @client = request.client
17
19
 
18
20
  validate
19
21
  end
@@ -25,7 +27,7 @@ module Doorkeeper
25
27
  end
26
28
 
27
29
  def validate_scopes
28
- return true unless @request.scopes.present?
30
+ return true if @request.scopes.blank?
29
31
 
30
32
  application_scopes = if @client.present?
31
33
  @client.application.scopes
@@ -4,7 +4,13 @@ module Doorkeeper
4
4
  include OAuth::Helpers
5
5
 
6
6
  def self.from_request(request, attributes = {})
7
- new(attributes.merge(name: request.error, state: request.try(:state)))
7
+ new(
8
+ attributes.merge(
9
+ name: request.error,
10
+ state: request.try(:state),
11
+ redirect_uri: request.try(:redirect_uri)
12
+ )
13
+ )
8
14
  end
9
15
 
10
16
  delegate :name, :description, :state, to: :@error
@@ -41,10 +47,12 @@ module Doorkeeper
41
47
  end
42
48
 
43
49
  def headers
44
- { 'Cache-Control' => 'no-store',
50
+ {
51
+ 'Cache-Control' => 'no-store',
45
52
  'Pragma' => 'no-cache',
46
53
  'Content-Type' => 'application/json; charset=utf-8',
47
- 'WWW-Authenticate' => authenticate_info }
54
+ 'WWW-Authenticate' => authenticate_info
55
+ }
48
56
  end
49
57
 
50
58
  protected
@@ -17,10 +17,6 @@ module Doorkeeper
17
17
  @valid_scopes.has_scopes?(parsed_scopes)
18
18
  end
19
19
 
20
- def match?
21
- valid? && parsed_scopes.has_scopes?(@valid_scopes)
22
- end
23
-
24
20
  private
25
21
 
26
22
  def valid_scopes(server_scopes, application_scopes)
@@ -35,10 +31,6 @@ module Doorkeeper
35
31
  def self.valid?(scope_str, server_scopes, application_scopes = nil)
36
32
  Validator.new(scope_str, server_scopes, application_scopes).valid?
37
33
  end
38
-
39
- def self.match?(scope_str, server_scopes, application_scopes = nil)
40
- Validator.new(scope_str, server_scopes, application_scopes).match?
41
- end
42
34
  end
43
35
  end
44
36
  end
@@ -16,6 +16,7 @@ module Doorkeeper
16
16
  @client = client
17
17
  @parameters = parameters
18
18
  @original_scopes = parameters[:scope]
19
+ @grant_type = Doorkeeper::OAuth::PASSWORD
19
20
  end
20
21
 
21
22
  private
@@ -26,16 +27,18 @@ module Doorkeeper
26
27
  end
27
28
 
28
29
  def validate_scopes
29
- return true unless @original_scopes.present?
30
- ScopeChecker.valid? @original_scopes, server.scopes, client.try(:scopes)
30
+ client_scopes = client.try(:scopes)
31
+ return true if scopes.blank?
32
+
33
+ ScopeChecker.valid?(scopes.to_s, server.scopes, client_scopes)
31
34
  end
32
35
 
33
36
  def validate_resource_owner
34
- !!resource_owner
37
+ !resource_owner.nil?
35
38
  end
36
39
 
37
40
  def validate_client
38
- !parameters[:client_id] || !!client
41
+ !parameters[:client_id] || !client.nil?
39
42
  end
40
43
  end
41
44
  end
@@ -7,17 +7,21 @@ module Doorkeeper
7
7
  validate :client, error: :invalid_client
8
8
  validate :scopes, error: :invalid_scope
9
9
  validate :redirect_uri, error: :invalid_redirect_uri
10
+ validate :code_challenge_method, error: :invalid_code_challenge_method
10
11
 
11
- attr_accessor :server, :client, :response_type, :redirect_uri, :state
12
+ attr_accessor :server, :client, :response_type, :redirect_uri, :state,
13
+ :code_challenge, :code_challenge_method
12
14
  attr_writer :scope
13
15
 
14
16
  def initialize(server, client, attrs = {})
15
- @server = server
16
- @client = client
17
- @response_type = attrs[:response_type]
18
- @redirect_uri = attrs[:redirect_uri]
19
- @scope = attrs[:scope]
20
- @state = attrs[:state]
17
+ @server = server
18
+ @client = client
19
+ @response_type = attrs[:response_type]
20
+ @redirect_uri = attrs[:redirect_uri]
21
+ @scope = attrs[:scope]
22
+ @state = attrs[:state]
23
+ @code_challenge = attrs[:code_challenge]
24
+ @code_challenge_method = attrs[:code_challenge_method]
21
25
  end
22
26
 
23
27
  def authorizable?
@@ -29,15 +33,36 @@ module Doorkeeper
29
33
  end
30
34
 
31
35
  def scope
32
- @scope.presence || server.default_scopes.to_s
36
+ @scope.presence || build_scopes
33
37
  end
34
38
 
35
39
  def error_response
36
40
  OAuth::ErrorResponse.from_request(self)
37
41
  end
38
42
 
43
+ def as_json(_options)
44
+ {
45
+ client_id: client.uid,
46
+ redirect_uri: redirect_uri,
47
+ state: state,
48
+ response_type: response_type,
49
+ scope: scope,
50
+ client_name: client.name,
51
+ status: I18n.t('doorkeeper.pre_authorization.status')
52
+ }
53
+ end
54
+
39
55
  private
40
56
 
57
+ def build_scopes
58
+ client_scopes = client.application.scopes
59
+ if client_scopes.blank?
60
+ server.default_scopes.to_s
61
+ else
62
+ (server.default_scopes & client_scopes).to_s
63
+ end
64
+ end
65
+
41
66
  def validate_response_type
42
67
  server.authorization_response_types.include? response_type
43
68
  end
@@ -47,7 +72,8 @@ module Doorkeeper
47
72
  end
48
73
 
49
74
  def validate_scopes
50
- return true unless scope.present?
75
+ return true if scope.blank?
76
+
51
77
  Helpers::ScopeChecker.valid?(
52
78
  scope,
53
79
  server.scopes,
@@ -55,14 +81,18 @@ module Doorkeeper
55
81
  )
56
82
  end
57
83
 
58
- # TODO: test uri should be matched against the client's one
59
84
  def validate_redirect_uri
60
85
  return false if redirect_uri.blank?
61
86
 
62
87
  Helpers::URIChecker.valid_for_authorization?(
63
- redirect_uri, client.redirect_uri
88
+ redirect_uri,
89
+ client.redirect_uri
64
90
  )
65
91
  end
92
+
93
+ def validate_code_challenge_method
94
+ !code_challenge.present? || (code_challenge_method.present? && code_challenge_method =~ /^plain$|^S256$/)
95
+ end
66
96
  end
67
97
  end
68
98
  end
@@ -65,7 +65,12 @@ module Doorkeeper
65
65
  end
66
66
 
67
67
  def access_token_expires_in
68
- Authorization::Token.access_token_expires_in(server, client)
68
+ Authorization::Token.access_token_expires_in(
69
+ server,
70
+ client,
71
+ Doorkeeper::OAuth::REFRESH_TOKEN,
72
+ scopes
73
+ )
69
74
  end
70
75
 
71
76
  def validate_token_presence
@@ -41,7 +41,7 @@ module Doorkeeper
41
41
  end
42
42
 
43
43
  def has_scopes?(scopes)
44
- scopes.all? { |s| exists?(s) }
44
+ scopes.all? { |scope| exists?(scope) }
45
45
  end
46
46
 
47
47
  def +(other)
@@ -3,7 +3,7 @@ module Doorkeeper
3
3
  class Token
4
4
  class << self
5
5
  def from_request(request, *methods)
6
- methods.inject(nil) do |credentials, method|
6
+ methods.inject(nil) do |_, method|
7
7
  method = self.method(method) if method.is_a?(Symbol)
8
8
  credentials = method.call(request)
9
9
  break credentials unless credentials.blank?
@@ -13,7 +13,10 @@ module Doorkeeper
13
13
  def authenticate(request, *methods)
14
14
  if (token = from_request(request, *methods))
15
15
  access_token = AccessToken.by_token(token)
16
- access_token.revoke_previous_refresh_token! if access_token
16
+ refresh_token_enabled = Doorkeeper.configuration.refresh_token_enabled?
17
+ if access_token.present? && refresh_token_enabled
18
+ access_token.revoke_previous_refresh_token!
19
+ end
17
20
  access_token
18
21
  end
19
22
  end
@@ -47,12 +47,12 @@ module Doorkeeper
47
47
 
48
48
  # Client Authentication
49
49
  def authorized_client
50
- @_authorized_client ||= server.credentials && server.client
50
+ @authorized_client ||= server.credentials && server.client
51
51
  end
52
52
 
53
53
  # Bearer Token Authentication
54
54
  def authorized_token
55
- @_authorized_token ||=
55
+ @authorized_token ||=
56
56
  OAuth::Token.authenticate(server.context.request, :from_bearer_authorization)
57
57
  end
58
58
 
@@ -23,9 +23,11 @@ module Doorkeeper
23
23
  end
24
24
 
25
25
  def headers
26
- { 'Cache-Control' => 'no-store',
26
+ {
27
+ 'Cache-Control' => 'no-store',
27
28
  'Pragma' => 'no-cache',
28
- 'Content-Type' => 'application/json; charset=utf-8' }
29
+ 'Content-Type' => 'application/json; charset=utf-8'
30
+ }
29
31
  end
30
32
  end
31
33
  end