doorkeeper 5.1.0 → 5.2.0.rc1

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +812 -0
  3. data/CONTRIBUTING.md +4 -9
  4. data/Dangerfile +1 -1
  5. data/Gemfile +2 -1
  6. data/NEWS.md +1 -814
  7. data/README.md +2 -2
  8. data/RELEASING.md +6 -5
  9. data/app/controllers/doorkeeper/applications_controller.rb +2 -0
  10. data/app/controllers/doorkeeper/tokens_controller.rb +18 -8
  11. data/app/validators/redirect_uri_validator.rb +19 -9
  12. data/app/views/doorkeeper/applications/_form.html.erb +0 -6
  13. data/app/views/doorkeeper/applications/show.html.erb +1 -1
  14. data/config/locales/en.yml +3 -1
  15. data/doorkeeper.gemspec +1 -1
  16. data/gemfiles/rails_5_0.gemfile +1 -0
  17. data/gemfiles/rails_5_1.gemfile +1 -0
  18. data/gemfiles/rails_5_2.gemfile +1 -0
  19. data/gemfiles/rails_6_0.gemfile +2 -1
  20. data/gemfiles/rails_master.gemfile +1 -0
  21. data/lib/doorkeeper/config/option.rb +13 -7
  22. data/lib/doorkeeper/config.rb +30 -3
  23. data/lib/doorkeeper/grape/helpers.rb +5 -1
  24. data/lib/doorkeeper/helpers/controller.rb +16 -3
  25. data/lib/doorkeeper/oauth/authorization/code.rb +10 -8
  26. data/lib/doorkeeper/oauth/authorization/token.rb +1 -1
  27. data/lib/doorkeeper/oauth/code_response.rb +2 -2
  28. data/lib/doorkeeper/oauth/error_response.rb +1 -1
  29. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +18 -4
  30. data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
  31. data/lib/doorkeeper/oauth/refresh_token_request.rb +8 -8
  32. data/lib/doorkeeper/oauth/token_introspection.rb +13 -12
  33. data/lib/doorkeeper/orm/active_record/access_grant.rb +1 -1
  34. data/lib/doorkeeper/orm/active_record/access_token.rb +2 -2
  35. data/lib/doorkeeper/orm/active_record/application.rb +7 -1
  36. data/lib/doorkeeper/orm/active_record.rb +17 -1
  37. data/lib/doorkeeper/stale_records_cleaner.rb +6 -2
  38. data/lib/doorkeeper/version.rb +2 -2
  39. data/lib/doorkeeper.rb +3 -0
  40. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +6 -6
  41. data/lib/generators/doorkeeper/templates/initializer.rb +41 -9
  42. data/lib/generators/doorkeeper/templates/migration.rb.erb +3 -0
  43. data/spec/controllers/applications_controller_spec.rb +93 -0
  44. data/spec/controllers/tokens_controller_spec.rb +71 -3
  45. data/spec/dummy/config/application.rb +3 -1
  46. data/spec/dummy/config/initializers/doorkeeper.rb +27 -9
  47. data/spec/lib/config_spec.rb +11 -0
  48. data/spec/lib/oauth/helpers/uri_checker_spec.rb +17 -2
  49. data/spec/lib/oauth/pre_authorization_spec.rb +0 -15
  50. data/spec/requests/flows/authorization_code_spec.rb +16 -4
  51. data/spec/requests/flows/revoke_token_spec.rb +19 -11
  52. data/spec/support/doorkeeper_rspec.rb +1 -1
  53. data/spec/validators/redirect_uri_validator_spec.rb +39 -14
  54. metadata +6 -13
  55. data/.coveralls.yml +0 -1
  56. data/.github/ISSUE_TEMPLATE.md +0 -25
  57. data/.github/PULL_REQUEST_TEMPLATE.md +0 -17
  58. data/.gitignore +0 -20
  59. data/.gitlab-ci.yml +0 -16
  60. data/.hound.yml +0 -3
  61. data/.rspec +0 -1
  62. data/.rubocop.yml +0 -50
  63. data/.travis.yml +0 -35
data/README.md CHANGED
@@ -146,10 +146,10 @@ bundle exec rake doorkeeper:server
146
146
  ````
147
147
 
148
148
  By default, it uses the latest Rails version with ActiveRecord. To run the
149
- tests with a specific ORM and Rails version:
149
+ tests with a specific Rails version:
150
150
 
151
151
  ```
152
- rails=5.2 orm=active_record bundle exec rake
152
+ BUNDLE_GEMFILE=gemfiles/rails_6_0.gemfile bundle exec rake
153
153
  ```
154
154
 
155
155
  ## Contributing
data/RELEASING.md CHANGED
@@ -1,10 +1,11 @@
1
- # Releasing doorkeeper
1
+ # Releasing Doorkeeper
2
2
 
3
- How to release doorkeeper in five easy steps!
3
+ How to release Doorkeeper in five easy steps!
4
4
 
5
5
  1. Update `lib/doorkeeper/version.rb` file accordingly.
6
- 2. Update `NEWS.md` to reflect the changes since last release.
7
- 3. Commit changes: `git commit -am 'Bump to vVERSION'`
8
- 4. Run `rake release`
6
+ 2. Update `CHANGELOG.md` to reflect the changes since last release.
7
+ 3. Commit changes: `git commit -am 'Bump to vVERSION'`.
8
+ 4. Build and publish the gem.
9
+ 4. Create GitHub release.
9
10
  5. Announce the new release, making sure to say “thank you” to the contributors
10
11
  who helped shape this version!
@@ -4,6 +4,7 @@ module Doorkeeper
4
4
  class ApplicationsController < Doorkeeper::ApplicationController
5
5
  layout "doorkeeper/admin" unless Doorkeeper.configuration.api_only
6
6
 
7
+ add_flash_types :application_secret
7
8
  before_action :authenticate_admin!
8
9
  before_action :set_application, only: %i[show edit update destroy]
9
10
 
@@ -32,6 +33,7 @@ module Doorkeeper
32
33
 
33
34
  if @application.save
34
35
  flash[:notice] = I18n.t(:notice, scope: %i[doorkeeper flash applications create])
36
+ flash[:application_secret] = @application.plaintext_secret
35
37
 
36
38
  respond_to do |format|
37
39
  format.html { redirect_to oauth_application_url(@application) }
@@ -6,8 +6,8 @@ module Doorkeeper
6
6
  headers.merge!(authorize_response.headers)
7
7
  render json: authorize_response.body,
8
8
  status: authorize_response.status
9
- rescue Errors::DoorkeeperError => error
10
- handle_token_exception(error)
9
+ rescue Errors::DoorkeeperError => e
10
+ handle_token_exception(e)
11
11
  end
12
12
 
13
13
  # OAuth 2.0 Token Revocation - http://tools.ietf.org/html/rfc7009
@@ -18,12 +18,13 @@ module Doorkeeper
18
18
  # Doorkeeper does not use the token_type_hint logic described in the
19
19
  # RFC 7009 due to the refresh token implementation that is a field in
20
20
  # the access token model.
21
- revoke_token if authorized?
22
21
 
23
- # The authorization server responds with HTTP status code 200 if the token
24
- # has been revoked successfully or if the client submitted an invalid
25
- # token
26
- render json: {}, status: 200
22
+ if authorized?
23
+ revoke_token
24
+ render json: {}, status: 200
25
+ else
26
+ render json: revocation_error_response, status: :forbidden
27
+ end
27
28
  end
28
29
 
29
30
  def introspect
@@ -71,7 +72,10 @@ module Doorkeeper
71
72
  end
72
73
 
73
74
  def revoke_token
74
- token.revoke if token.accessible?
75
+ # The authorization server responds with HTTP status code 200 if the token
76
+ # has been revoked successfully or if the client submitted an invalid
77
+ # token
78
+ token.revoke if token&.accessible?
75
79
  end
76
80
 
77
81
  def token
@@ -86,5 +90,11 @@ module Doorkeeper
86
90
  def authorize_response
87
91
  @authorize_response ||= strategy.authorize
88
92
  end
93
+
94
+ def revocation_error_response
95
+ error_description = I18n.t(:unauthorized, scope: %i[doorkeeper errors messages revoke])
96
+
97
+ { error: :unauthorized_client, error_description: error_description }
98
+ end
89
99
  end
90
100
  end
@@ -2,11 +2,10 @@
2
2
 
3
3
  require "uri"
4
4
 
5
+ # ActiveModel validator for redirect URI validation in according
6
+ # to OAuth standards and Doorkeeper configuration.
7
+ #
5
8
  class RedirectUriValidator < ActiveModel::EachValidator
6
- def self.native_redirect_uri
7
- Doorkeeper.configuration.native_redirect_uri
8
- end
9
-
10
9
  def validate_each(record, attribute, value)
11
10
  if value.blank?
12
11
  return if Doorkeeper.configuration.allow_blank_redirect_uri?(record)
@@ -14,12 +13,13 @@ class RedirectUriValidator < ActiveModel::EachValidator
14
13
  record.errors.add(attribute, :blank)
15
14
  else
16
15
  value.split.each do |val|
17
- uri = ::URI.parse(val)
18
- next if native_redirect_uri?(uri)
16
+ next if oob_redirect_uri?(val)
19
17
 
18
+ uri = ::URI.parse(val)
20
19
  record.errors.add(attribute, :forbidden_uri) if forbidden_uri?(uri)
21
20
  record.errors.add(attribute, :fragment_present) unless uri.fragment.nil?
22
- record.errors.add(attribute, :relative_uri) if uri.scheme.nil? || uri.host.nil?
21
+ record.errors.add(attribute, :unspecified_scheme) if unspecified_scheme?(uri)
22
+ record.errors.add(attribute, :relative_uri) if relative_uri?(uri)
23
23
  record.errors.add(attribute, :secured_uri) if invalid_ssl_uri?(uri)
24
24
  end
25
25
  end
@@ -29,14 +29,24 @@ class RedirectUriValidator < ActiveModel::EachValidator
29
29
 
30
30
  private
31
31
 
32
- def native_redirect_uri?(uri)
33
- self.class.native_redirect_uri.present? && uri.to_s == self.class.native_redirect_uri.to_s
32
+ def oob_redirect_uri?(uri)
33
+ Doorkeeper::OAuth::NonStandard::IETF_WG_OAUTH2_OOB_METHODS.include?(uri)
34
34
  end
35
35
 
36
36
  def forbidden_uri?(uri)
37
37
  Doorkeeper.configuration.forbid_redirect_uri.call(uri)
38
38
  end
39
39
 
40
+ def unspecified_scheme?(uri)
41
+ return true if uri.opaque.present?
42
+
43
+ %w[localhost].include?(uri.try(:scheme))
44
+ end
45
+
46
+ def relative_uri?(uri)
47
+ uri.scheme.nil? && uri.host.nil?
48
+ end
49
+
40
50
  def invalid_ssl_uri?(uri)
41
51
  forces_ssl = Doorkeeper.configuration.force_ssl_in_redirect_uri
42
52
  non_https = uri.try(:scheme) == "http"
@@ -20,12 +20,6 @@
20
20
  <%= t('doorkeeper.applications.help.redirect_uri') %>
21
21
  </span>
22
22
 
23
- <% if Doorkeeper.configuration.native_redirect_uri %>
24
- <span class="form-text text-secondary">
25
- <%= raw t('doorkeeper.applications.help.native_redirect_uri', native_redirect_uri: content_tag(:code, class: 'bg-light') { Doorkeeper.configuration.native_redirect_uri }) %>
26
- </span>
27
- <% end %>
28
-
29
23
  <% if Doorkeeper.configuration.allow_blank_redirect_uri?(application) %>
30
24
  <span class="form-text text-secondary">
31
25
  <%= t('doorkeeper.applications.help.blank_redirect_uri') %>
@@ -8,7 +8,7 @@
8
8
  <p><code class="bg-light" id="application_id"><%= @application.uid %></code></p>
9
9
 
10
10
  <h4><%= t('.secret') %>:</h4>
11
- <p><code class="bg-light" id="secret"><%= @application.plaintext_secret %></code></p>
11
+ <p><code class="bg-light" id="secret"><%= flash[:application_secret].presence || @application.plaintext_secret %></code></p>
12
12
 
13
13
  <h4><%= t('.scopes') %>:</h4>
14
14
  <p><code class="bg-light" id="scopes"><%= @application.scopes.presence || raw('&nbsp;') %></code></p>
@@ -11,6 +11,7 @@ en:
11
11
  redirect_uri:
12
12
  fragment_present: 'cannot contain a fragment.'
13
13
  invalid_uri: 'must be a valid URI.'
14
+ unspecified_scheme: 'must specify a scheme.'
14
15
  relative_uri: 'must be an absolute URI.'
15
16
  secured_uri: 'must be an HTTPS/SSL URI.'
16
17
  forbidden_uri: 'is forbidden by the server.'
@@ -33,7 +34,6 @@ en:
33
34
  confidential: 'Application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential.'
34
35
  redirect_uri: 'Use one line per URI'
35
36
  blank_redirect_uri: "Leave it blank if you configured your provider to use Client Credentials, Resource Owner Password Credentials or any other grant type that doesn't require redirect URI."
36
- native_redirect_uri: 'Use %{native_redirect_uri} if you want to add localhost URIs for development purposes'
37
37
  scopes: 'Separate scopes with spaces. Leave blank to use the default scopes.'
38
38
  edit:
39
39
  title: 'Edit application'
@@ -114,6 +114,8 @@ en:
114
114
  revoked: "The access token was revoked"
115
115
  expired: "The access token expired"
116
116
  unknown: "The access token is invalid"
117
+ revoke:
118
+ unauthorized: "You are not authorized to revoke this token"
117
119
 
118
120
  flash:
119
121
  applications:
data/doorkeeper.gemspec CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |gem|
14
14
  gem.description = "Doorkeeper is an OAuth 2 provider for Rails and Grape."
15
15
  gem.license = "MIT"
16
16
 
17
- gem.files = `git ls-files`.split("\n")
17
+ gem.files = `git ls-files`.split("\n").reject { |file| file.start_with?(".") }
18
18
  gem.test_files = `git ls-files -- spec/*`.split("\n")
19
19
  gem.require_paths = ["lib"]
20
20
 
@@ -9,6 +9,7 @@ gem "rspec-mocks", git: "https://github.com/rspec/rspec-mocks.git"
9
9
  gem "rspec-rails", branch: "4-0-dev", git: "https://github.com/rspec/rspec-rails.git"
10
10
  gem "rspec-support", git: "https://github.com/rspec/rspec-support.git"
11
11
  gem "rubocop", "~> 0.66"
12
+ gem "rubocop-performance"
12
13
  gem "bcrypt", "~> 3.1", require: false
13
14
  gem "activerecord-jdbcsqlite3-adapter", platform: :jruby
14
15
  gem "sqlite3", "~> 1.3", "< 1.4", platform: [:ruby, :mswin, :mingw, :x64_mingw]
@@ -9,6 +9,7 @@ gem "rspec-mocks", git: "https://github.com/rspec/rspec-mocks.git"
9
9
  gem "rspec-rails", branch: "4-0-dev", git: "https://github.com/rspec/rspec-rails.git"
10
10
  gem "rspec-support", git: "https://github.com/rspec/rspec-support.git"
11
11
  gem "rubocop", "~> 0.66"
12
+ gem "rubocop-performance"
12
13
  gem "bcrypt", "~> 3.1", require: false
13
14
  gem "activerecord-jdbcsqlite3-adapter", platform: :jruby
14
15
  gem "sqlite3", "~> 1.3", "< 1.4", platform: [:ruby, :mswin, :mingw, :x64_mingw]
@@ -9,6 +9,7 @@ gem "rspec-mocks", git: "https://github.com/rspec/rspec-mocks.git"
9
9
  gem "rspec-rails", branch: "4-0-dev", git: "https://github.com/rspec/rspec-rails.git"
10
10
  gem "rspec-support", git: "https://github.com/rspec/rspec-support.git"
11
11
  gem "rubocop", "~> 0.66"
12
+ gem "rubocop-performance"
12
13
  gem "bcrypt", "~> 3.1", require: false
13
14
  gem "activerecord-jdbcsqlite3-adapter", platform: :jruby
14
15
  gem "sqlite3", "~> 1.3", "< 1.4", platform: [:ruby, :mswin, :mingw, :x64_mingw]
@@ -2,13 +2,14 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "~> 6.0.0.beta3"
5
+ gem "rails", "~> 6.0.0.rc1"
6
6
  gem "rspec-core", git: "https://github.com/rspec/rspec-core.git"
7
7
  gem "rspec-expectations", git: "https://github.com/rspec/rspec-expectations.git"
8
8
  gem "rspec-mocks", git: "https://github.com/rspec/rspec-mocks.git"
9
9
  gem "rspec-rails", branch: "4-0-dev", git: "https://github.com/rspec/rspec-rails.git"
10
10
  gem "rspec-support", git: "https://github.com/rspec/rspec-support.git"
11
11
  gem "rubocop", "~> 0.66"
12
+ gem "rubocop-performance"
12
13
  gem "bcrypt", "~> 3.1", require: false
13
14
  gem "activerecord-jdbcsqlite3-adapter", platform: :jruby
14
15
  gem "sqlite3", "~> 1.4", platform: [:ruby, :mswin, :mingw, :x64_mingw]
@@ -9,6 +9,7 @@ gem "rspec-mocks", git: "https://github.com/rspec/rspec-mocks.git"
9
9
  gem "rspec-rails", branch: "4-0-dev", git: "https://github.com/rspec/rspec-rails.git"
10
10
  gem "rspec-support", git: "https://github.com/rspec/rspec-support.git"
11
11
  gem "rubocop", "~> 0.66"
12
+ gem "rubocop-performance"
12
13
  gem "bcrypt", "~> 3.1", require: false
13
14
  gem "activerecord-jdbcsqlite3-adapter", platform: :jruby
14
15
  gem "sqlite3", "~> 1.4", platform: [:ruby, :mswin, :mingw, :x64_mingw]
@@ -38,14 +38,20 @@ module Doorkeeper
38
38
 
39
39
  Builder.instance_eval do
40
40
  remove_method name if method_defined?(name)
41
- define_method name do |*args, &block|
42
- value = if attribute_builder
43
- attribute_builder.new(&block).build
44
- else
45
- block || args.first
46
- end
41
+ if options[:deprecated]
42
+ define_method name do |*_, &_|
43
+ Kernel.warn "[DOORKEEPER] #{name} has been deprecated and will soon be removed"
44
+ end
45
+ else
46
+ define_method name do |*args, &block|
47
+ value = if attribute_builder
48
+ attribute_builder.new(&block).build
49
+ else
50
+ block || args.first
51
+ end
47
52
 
48
- @config.instance_variable_set(:"@#{attribute}", value)
53
+ @config.instance_variable_set(:"@#{attribute}", value)
54
+ end
49
55
  end
50
56
  end
51
57
 
@@ -25,8 +25,8 @@ module Doorkeeper
25
25
 
26
26
  def self.setup_orm_adapter
27
27
  @orm_adapter = "doorkeeper/orm/#{configuration.orm}".classify.constantize
28
- rescue NameError => error
29
- raise error, "ORM adapter not found (#{configuration.orm})", <<-ERROR_MSG.strip_heredoc
28
+ rescue NameError => e
29
+ raise e, "ORM adapter not found (#{configuration.orm})", <<-ERROR_MSG.strip_heredoc
30
30
  [doorkeeper] ORM adapter not found (#{configuration.orm}), or there was an error
31
31
  trying to load it.
32
32
 
@@ -254,7 +254,7 @@ module Doorkeeper
254
254
  option :custom_access_token_expires_in, default: ->(_context) { nil }
255
255
  option :authorization_code_expires_in, default: 600
256
256
  option :orm, default: :active_record
257
- option :native_redirect_uri, default: "urn:ietf:wg:oauth:2.0:oob"
257
+ option :native_redirect_uri, default: "urn:ietf:wg:oauth:2.0:oob", deprecated: true
258
258
  option :active_record_options, default: {}
259
259
  option :grant_flows, default: %w[authorization_code client_credentials]
260
260
  option :handle_auth_errors, default: :render
@@ -321,6 +321,33 @@ module Doorkeeper
321
321
  grant_flows.exclude?("implicit")
322
322
  end)
323
323
 
324
+ # Configure protection of token introspection request.
325
+ # By default token introspection is allowed only for clients that
326
+ # introspected token belongs to or if access token been introspected
327
+ # is a public one (doesn't belong to any client).
328
+ #
329
+ # You can define any custom rule you need or just disable token
330
+ # introspection at all.
331
+ #
332
+ # @param token [Doorkeeper::AccessToken]
333
+ # token to be introspected
334
+ #
335
+ # @param authorized_client [Doorkeeper::Application]
336
+ # authorized client (if request is authorized using Basic auth with
337
+ # Client Credentials for example)
338
+ #
339
+ # @param authorized_token [Doorkeeper::AccessToken]
340
+ # Bearer token used to authorize the request
341
+ #
342
+ option :allow_token_introspection,
343
+ default: (lambda do |token, authorized_client, _authorized_token|
344
+ if token.application
345
+ token.application == authorized_client
346
+ else
347
+ true
348
+ end
349
+ end)
350
+
324
351
  attr_reader :api_only,
325
352
  :enforce_content_type,
326
353
  :reuse_access_token,
@@ -4,6 +4,8 @@ require "doorkeeper/grape/authorization_decorator"
4
4
 
5
5
  module Doorkeeper
6
6
  module Grape
7
+ # Doorkeeper helpers for Grape applications.
8
+ # Provides helpers for endpoints authorization based on defined set of scopes.
7
9
  module Helpers
8
10
  # These helpers are for grape >= 0.10
9
11
  extend ::Grape::API::Helpers
@@ -11,7 +13,9 @@ module Doorkeeper
11
13
 
12
14
  # endpoint specific scopes > parameter scopes > default scopes
13
15
  def doorkeeper_authorize!(*scopes)
14
- endpoint_scopes = endpoint.route_setting(:scopes) || endpoint.options[:route_options][:scopes]
16
+ endpoint_scopes = endpoint.route_setting(:scopes) ||
17
+ endpoint.options[:route_options][:scopes]
18
+
15
19
  scopes = if endpoint_scopes
16
20
  Doorkeeper::OAuth::Scopes.from_array(endpoint_scopes)
17
21
  elsif scopes && !scopes.empty?
@@ -4,6 +4,8 @@
4
4
  # Doorkeeper::ApplicationMetalController or Doorkeeper::ApplicationController
5
5
  module Doorkeeper
6
6
  module Helpers
7
+ # Rails controller helpers.
8
+ #
7
9
  module Controller
8
10
  private
9
11
 
@@ -40,7 +42,11 @@ module Doorkeeper
40
42
  end
41
43
 
42
44
  def get_error_response_from_exception(exception)
43
- OAuth::ErrorResponse.new name: exception.type, state: params[:state]
45
+ if exception.respond_to?(:response)
46
+ exception.response
47
+ else
48
+ OAuth::ErrorResponse.new name: exception.type, state: params[:state]
49
+ end
44
50
  end
45
51
 
46
52
  def handle_token_exception(exception)
@@ -51,14 +57,21 @@ module Doorkeeper
51
57
  end
52
58
 
53
59
  def skip_authorization?
54
- !!instance_exec([@server.current_resource_owner, @pre_auth.client], &Doorkeeper.configuration.skip_authorization)
60
+ !!instance_exec(
61
+ [@server.current_resource_owner, @pre_auth.client],
62
+ &Doorkeeper.configuration.skip_authorization
63
+ )
55
64
  end
56
65
 
57
66
  def enforce_content_type
58
- if (request.put? || request.post? || request.patch?) && request.content_type != "application/x-www-form-urlencoded"
67
+ if (request.put? || request.post? || request.patch?) && !x_www_form_urlencoded?
59
68
  render json: {}, status: :unsupported_media_type
60
69
  end
61
70
  end
71
+
72
+ def x_www_form_urlencoded?
73
+ request.content_type == "application/x-www-form-urlencoded"
74
+ end
62
75
  end
63
76
  end
64
77
  end
@@ -12,10 +12,10 @@ module Doorkeeper
12
12
  end
13
13
 
14
14
  def issue_token
15
- @token ||= AccessGrant.create! access_grant_attributes
15
+ @token ||= AccessGrant.create!(access_grant_attributes)
16
16
  end
17
17
 
18
- def native_redirect
18
+ def oob_redirect
19
19
  { action: :show, code: token.plaintext_token }
20
20
  end
21
21
 
@@ -30,11 +30,13 @@ module Doorkeeper
30
30
  end
31
31
 
32
32
  def access_grant_attributes
33
- pkce_attributes.merge application_id: pre_auth.client.id,
34
- resource_owner_id: resource_owner.id,
35
- expires_in: authorization_code_expires_in,
36
- redirect_uri: pre_auth.redirect_uri,
37
- scopes: pre_auth.scopes.to_s
33
+ pkce_attributes.merge(
34
+ application_id: pre_auth.client.id,
35
+ resource_owner_id: resource_owner.id,
36
+ expires_in: authorization_code_expires_in,
37
+ redirect_uri: pre_auth.redirect_uri,
38
+ scopes: pre_auth.scopes.to_s
39
+ )
38
40
  end
39
41
 
40
42
  def pkce_attributes
@@ -46,7 +48,7 @@ module Doorkeeper
46
48
  }
47
49
  end
48
50
 
49
- # ensures firstly, if migration with additional pcke columns was
51
+ # Ensures firstly, if migration with additional PKCE columns was
50
52
  # generated and migrated
51
53
  def pkce_supported?
52
54
  Doorkeeper::AccessGrant.pkce_supported?
@@ -63,7 +63,7 @@ module Doorkeeper
63
63
  )
64
64
  end
65
65
 
66
- def native_redirect
66
+ def oob_redirect
67
67
  {
68
68
  controller: controller,
69
69
  action: :show,
@@ -18,8 +18,8 @@ module Doorkeeper
18
18
  end
19
19
 
20
20
  def redirect_uri
21
- if URIChecker.native_uri? pre_auth.redirect_uri
22
- auth.native_redirect
21
+ if URIChecker.oob_uri? pre_auth.redirect_uri
22
+ auth.oob_redirect
23
23
  elsif response_on_fragment
24
24
  Authorization::URIBuilder.uri_with_fragment(
25
25
  pre_auth.redirect_uri,
@@ -41,7 +41,7 @@ module Doorkeeper
41
41
 
42
42
  def redirectable?
43
43
  name != :invalid_redirect_uri && name != :invalid_client &&
44
- !URIChecker.native_uri?(@redirect_uri)
44
+ !URIChecker.oob_uri?(@redirect_uri)
45
45
  end
46
46
 
47
47
  def redirect_uri
@@ -25,10 +25,10 @@ module Doorkeeper
25
25
  module Helpers
26
26
  module URIChecker
27
27
  def self.valid?(url)
28
- return true if native_uri?(url)
28
+ return true if oob_uri?(url)
29
29
 
30
30
  uri = as_uri(url)
31
- uri.fragment.nil? && !uri.host.nil? && !uri.scheme.nil?
31
+ valid_scheme?(uri) && iff_host?(uri) && uri.fragment.nil? && uri.opaque.nil?
32
32
  rescue URI::InvalidURIError
33
33
  false
34
34
  end
@@ -78,8 +78,22 @@ module Doorkeeper
78
78
  client_query.split("&").sort == query.split("&").sort
79
79
  end
80
80
 
81
- def self.native_uri?(url)
82
- url == Doorkeeper.configuration.native_redirect_uri
81
+ def self.valid_scheme?(uri)
82
+ return false if uri.scheme.nil?
83
+
84
+ %w[localhost].include?(uri.scheme) == false
85
+ end
86
+
87
+ def self.hypertext_scheme?(uri)
88
+ %w[http https].include?(uri.scheme)
89
+ end
90
+
91
+ def self.iff_host?(uri)
92
+ !(hypertext_scheme?(uri) && uri.host.nil?)
93
+ end
94
+
95
+ def self.oob_uri?(uri)
96
+ NonStandard::IETF_WG_OAUTH2_OOB_METHODS.include?(uri)
83
97
  end
84
98
  end
85
99
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module OAuth
5
+ class NonStandard
6
+ # These are not part of the OAuth 2 specification but are still in use by Google
7
+ # and in some other implementations. Native applications should use one of the
8
+ # approaches discussed in RFC8252. OOB is 'Out of Band'
9
+
10
+ # This value signals to the Google Authorization Server that the authorization
11
+ # code should be returned in the title bar of the browser, with the page text
12
+ # prompting the user to copy the code and paste it in the application.
13
+ # This is useful when the client (such as a Windows application) cannot listen
14
+ # on an HTTP port without significant client configuration.
15
+
16
+ # When you use this value, your application can then detect that the page has loaded, and can
17
+ # read the title of the HTML page to obtain the authorization code. It is then up to your
18
+ # application to close the browser window if you want to ensure that the user never sees the
19
+ # page that contains the authorization code. The mechanism for doing this varies from platform
20
+ # to platform.
21
+ #
22
+ # If your platform doesn't allow you to detect that the page has loaded or read the title of
23
+ # the page, you can have the user paste the code back to your application, as prompted by the
24
+ # text in the confirmation page that the OAuth 2.0 server generates.
25
+ IETF_WG_OAUTH2_OOB = "urn:ietf:wg:oauth:2.0:oob"
26
+
27
+ # This is identical to urn:ietf:wg:oauth:2.0:oob, but the text in the confirmation page that
28
+ # the OAuth 2.0 server generates won't instruct the user to copy the authorization code, but
29
+ # instead will simply ask the user to close the window.
30
+ #
31
+ # This is useful when your application reads the title of the HTML page (by checking window
32
+ # titles on the desktop, for example) to obtain the authorization code, but can't close the
33
+ # page on its own.
34
+ IETF_WG_OAUTH2_OOB_AUTO = "urn:ietf:wg:oauth:2.0:oob:auto"
35
+
36
+ IETF_WG_OAUTH2_OOB_METHODS = [IETF_WG_OAUTH2_OOB, IETF_WG_OAUTH2_OOB_AUTO].freeze
37
+ end
38
+ end
39
+ end
@@ -15,20 +15,20 @@ module Doorkeeper
15
15
  :server
16
16
 
17
17
  def initialize(server, refresh_token, credentials, parameters = {})
18
- @server = server
19
- @refresh_token = refresh_token
20
- @credentials = credentials
18
+ @server = server
19
+ @refresh_token = refresh_token
20
+ @credentials = credentials
21
21
  @original_scopes = parameters[:scope] || parameters[:scopes]
22
22
  @refresh_token_parameter = parameters[:refresh_token]
23
-
24
- if credentials
25
- @client = Application.by_uid_and_secret credentials.uid,
26
- credentials.secret
27
- end
23
+ @client = load_client(credentials) if credentials
28
24
  end
29
25
 
30
26
  private
31
27
 
28
+ def load_client(credentials)
29
+ Application.by_uid_and_secret(credentials.uid, credentials.secret)
30
+ end
31
+
32
32
  def before_successful_response
33
33
  refresh_token.transaction do
34
34
  refresh_token.lock!
@@ -80,8 +80,7 @@ module Doorkeeper
80
80
 
81
81
  # Bearer Token Authentication
82
82
  def authorized_token
83
- @authorized_token ||=
84
- OAuth::Token.authenticate(server.context.request, :from_bearer_authorization)
83
+ @authorized_token ||= Doorkeeper.authenticate(server.context.request)
85
84
  end
86
85
 
87
86
  # 2.2. Introspection Response
@@ -150,9 +149,9 @@ module Doorkeeper
150
149
  #
151
150
  def active?
152
151
  if authorized_client
153
- valid_token? && authorized_for_client?
152
+ valid_token? && token_introspection_allowed?(authorized_client.application)
154
153
  else
155
- valid_token?
154
+ valid_token? && token_introspection_allowed?(authorized_token&.application)
156
155
  end
157
156
  end
158
157
 
@@ -166,14 +165,16 @@ module Doorkeeper
166
165
  authorized_token.token == @token&.token
167
166
  end
168
167
 
169
- # If token doesn't belong to some client, then it is public.
170
- # Otherwise in it required for token to be connected to the same client.
171
- def authorized_for_client?
172
- if @token.application
173
- @token.application == authorized_client.application
174
- else
175
- true
176
- end
168
+ # config constraints for introspection in Doorkeeper.configuration.allow_token_introspection
169
+ def token_introspection_allowed?(client)
170
+ allow_introspection = Doorkeeper.configuration.allow_token_introspection
171
+ return allow_introspection unless allow_introspection.respond_to?(:call)
172
+
173
+ allow_introspection.call(
174
+ @token,
175
+ client,
176
+ authorized_token
177
+ )
177
178
  end
178
179
 
179
180
  # Allows to customize introspection response.