doorkeeper 5.1.0 → 5.2.0.rc2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +825 -0
- data/CONTRIBUTING.md +11 -9
- data/Dangerfile +2 -2
- data/Dockerfile +29 -0
- data/Gemfile +2 -1
- data/NEWS.md +1 -814
- data/README.md +2 -2
- data/RELEASING.md +6 -5
- data/app/controllers/doorkeeper/applications_controller.rb +2 -0
- data/app/controllers/doorkeeper/authorizations_controller.rb +6 -1
- data/app/controllers/doorkeeper/tokens_controller.rb +32 -9
- data/app/validators/redirect_uri_validator.rb +19 -9
- data/app/views/doorkeeper/applications/_form.html.erb +0 -6
- data/app/views/doorkeeper/applications/show.html.erb +1 -1
- data/config/locales/en.yml +3 -1
- data/doorkeeper.gemspec +1 -1
- data/gemfiles/rails_5_0.gemfile +1 -0
- data/gemfiles/rails_5_1.gemfile +1 -0
- data/gemfiles/rails_5_2.gemfile +1 -0
- data/gemfiles/rails_6_0.gemfile +2 -1
- data/gemfiles/rails_master.gemfile +1 -0
- data/lib/doorkeeper/config/option.rb +13 -7
- data/lib/doorkeeper/config.rb +33 -3
- data/lib/doorkeeper/grape/helpers.rb +5 -1
- data/lib/doorkeeper/helpers/controller.rb +16 -3
- data/lib/doorkeeper/models/access_token_mixin.rb +43 -2
- data/lib/doorkeeper/oauth/authorization/code.rb +10 -8
- data/lib/doorkeeper/oauth/authorization/token.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +14 -0
- data/lib/doorkeeper/oauth/code_response.rb +2 -2
- data/lib/doorkeeper/oauth/error_response.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +18 -4
- data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
- data/lib/doorkeeper/oauth/refresh_token_request.rb +8 -8
- data/lib/doorkeeper/oauth/token_introspection.rb +19 -12
- data/lib/doorkeeper/orm/active_record/access_grant.rb +1 -1
- data/lib/doorkeeper/orm/active_record/access_token.rb +2 -2
- data/lib/doorkeeper/orm/active_record/application.rb +7 -1
- data/lib/doorkeeper/orm/active_record.rb +17 -1
- data/lib/doorkeeper/stale_records_cleaner.rb +6 -2
- data/lib/doorkeeper/version.rb +2 -2
- data/lib/doorkeeper.rb +3 -0
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +6 -6
- data/lib/generators/doorkeeper/templates/initializer.rb +57 -12
- data/lib/generators/doorkeeper/templates/migration.rb.erb +3 -0
- data/spec/controllers/applications_controller_spec.rb +93 -0
- data/spec/controllers/authorizations_controller_spec.rb +13 -0
- data/spec/controllers/tokens_controller_spec.rb +205 -37
- data/spec/dummy/config/application.rb +3 -1
- data/spec/dummy/config/initializers/doorkeeper.rb +54 -9
- data/spec/lib/config_spec.rb +11 -0
- data/spec/lib/oauth/client_credentials/creator_spec.rb +3 -0
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +17 -2
- data/spec/lib/oauth/pre_authorization_spec.rb +0 -15
- data/spec/requests/flows/authorization_code_spec.rb +16 -4
- data/spec/requests/flows/revoke_token_spec.rb +19 -11
- data/spec/support/doorkeeper_rspec.rb +1 -1
- data/spec/validators/redirect_uri_validator_spec.rb +39 -14
- metadata +7 -13
- data/.coveralls.yml +0 -1
- data/.github/ISSUE_TEMPLATE.md +0 -25
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -17
- data/.gitignore +0 -20
- data/.gitlab-ci.yml +0 -16
- data/.hound.yml +0 -3
- data/.rspec +0 -1
- data/.rubocop.yml +0 -50
- 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
|
|
149
|
+
tests with a specific Rails version:
|
|
150
150
|
|
|
151
151
|
```
|
|
152
|
-
|
|
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
|
|
1
|
+
# Releasing Doorkeeper
|
|
2
2
|
|
|
3
|
-
How to release
|
|
3
|
+
How to release Doorkeeper in five easy steps!
|
|
4
4
|
|
|
5
5
|
1. Update `lib/doorkeeper/version.rb` file accordingly.
|
|
6
|
-
2. Update `
|
|
7
|
-
3. Commit changes: `git commit -am 'Bump to vVERSION'
|
|
8
|
-
4.
|
|
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) }
|
|
@@ -68,7 +68,12 @@ module Doorkeeper
|
|
|
68
68
|
def pre_auth
|
|
69
69
|
@pre_auth ||= OAuth::PreAuthorization.new(Doorkeeper.configuration,
|
|
70
70
|
server.client_via_uid,
|
|
71
|
-
|
|
71
|
+
pre_auth_params)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def pre_auth_params
|
|
75
|
+
params.permit(:response_type, :redirect_uri, :scope, :state,
|
|
76
|
+
:code_challenge, :code_challenge_method)
|
|
72
77
|
end
|
|
73
78
|
|
|
74
79
|
def authorization
|
|
@@ -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 =>
|
|
10
|
-
handle_token_exception(
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
|
@@ -84,7 +88,26 @@ module Doorkeeper
|
|
|
84
88
|
end
|
|
85
89
|
|
|
86
90
|
def authorize_response
|
|
87
|
-
@authorize_response ||=
|
|
91
|
+
@authorize_response ||= begin
|
|
92
|
+
before_successful_authorization
|
|
93
|
+
auth = strategy.authorize
|
|
94
|
+
after_successful_authorization unless auth.is_a?(Doorkeeper::OAuth::ErrorResponse)
|
|
95
|
+
auth
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def after_successful_authorization
|
|
100
|
+
Doorkeeper.configuration.after_successful_authorization.call(self)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def before_successful_authorization
|
|
104
|
+
Doorkeeper.configuration.before_successful_authorization.call(self)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def revocation_error_response
|
|
108
|
+
error_description = I18n.t(:unauthorized, scope: %i[doorkeeper errors messages revoke])
|
|
109
|
+
|
|
110
|
+
{ error: :unauthorized_client, error_description: error_description }
|
|
88
111
|
end
|
|
89
112
|
end
|
|
90
113
|
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
|
-
|
|
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, :
|
|
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
|
|
33
|
-
|
|
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(' ') %></code></p>
|
data/config/locales/en.yml
CHANGED
|
@@ -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
|
|
data/gemfiles/rails_5_0.gemfile
CHANGED
|
@@ -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]
|
data/gemfiles/rails_5_1.gemfile
CHANGED
|
@@ -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]
|
data/gemfiles/rails_5_2.gemfile
CHANGED
|
@@ -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]
|
data/gemfiles/rails_6_0.gemfile
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
source "https://rubygems.org"
|
|
4
4
|
|
|
5
|
-
gem "rails", "~> 6.0.0.
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
53
|
+
@config.instance_variable_set(:"@#{attribute}", value)
|
|
54
|
+
end
|
|
49
55
|
end
|
|
50
56
|
end
|
|
51
57
|
|
data/lib/doorkeeper/config.rb
CHANGED
|
@@ -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 =>
|
|
29
|
-
raise
|
|
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,36 @@ module Doorkeeper
|
|
|
321
321
|
grant_flows.exclude?("implicit")
|
|
322
322
|
end)
|
|
323
323
|
|
|
324
|
+
# Configure protection of token introspection request.
|
|
325
|
+
# By default this configuration allows to introspect a token by
|
|
326
|
+
# another token of the same application, or to introspect the token
|
|
327
|
+
# that belongs to authorized client, or access token has been introspected
|
|
328
|
+
# is a public one (doesn't belong to any client)
|
|
329
|
+
#
|
|
330
|
+
# You can define any custom rule you need or just disable token
|
|
331
|
+
# introspection at all.
|
|
332
|
+
#
|
|
333
|
+
# @param token [Doorkeeper::AccessToken]
|
|
334
|
+
# token to be introspected
|
|
335
|
+
#
|
|
336
|
+
# @param authorized_client [Doorkeeper::Application]
|
|
337
|
+
# authorized client (if request is authorized using Basic auth with
|
|
338
|
+
# Client Credentials for example)
|
|
339
|
+
#
|
|
340
|
+
# @param authorized_token [Doorkeeper::AccessToken]
|
|
341
|
+
# Bearer token used to authorize the request
|
|
342
|
+
#
|
|
343
|
+
option :allow_token_introspection,
|
|
344
|
+
default: (lambda do |token, authorized_client, authorized_token|
|
|
345
|
+
if authorized_token
|
|
346
|
+
authorized_token.application == token&.application
|
|
347
|
+
elsif token.application
|
|
348
|
+
authorized_client == token.application
|
|
349
|
+
else
|
|
350
|
+
true
|
|
351
|
+
end
|
|
352
|
+
end)
|
|
353
|
+
|
|
324
354
|
attr_reader :api_only,
|
|
325
355
|
:enforce_content_type,
|
|
326
356
|
: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) ||
|
|
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
|
-
|
|
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(
|
|
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?) &&
|
|
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
|
|
@@ -76,9 +76,50 @@ module Doorkeeper
|
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
tokens = authorized_tokens_for(application.try(:id), resource_owner_id)
|
|
79
|
-
tokens
|
|
80
|
-
|
|
79
|
+
find_matching_token(tokens, application, scopes)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Interface to enumerate access token records in batches in order not
|
|
83
|
+
# to bloat the memory. Could be overloaded in any ORM extension.
|
|
84
|
+
#
|
|
85
|
+
def find_access_token_in_batches(relation, *args, &block)
|
|
86
|
+
relation.find_in_batches(*args, &block)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Enumerates AccessToken records in batches to find a matching token.
|
|
90
|
+
# Batching is required in order not to pollute the memory if Application
|
|
91
|
+
# has huge amount of associated records.
|
|
92
|
+
#
|
|
93
|
+
# ActiveRecord 5.x - 6.x ignores custom ordering so we can't perform a
|
|
94
|
+
# database sort by created_at, so we need to load all the matching records,
|
|
95
|
+
# sort them and find latest one. Probably it would be better to rewrite this
|
|
96
|
+
# query using Time math if possible, but we n eed to consider ORM and
|
|
97
|
+
# different databases support.
|
|
98
|
+
#
|
|
99
|
+
# @param relation [ActiveRecord::Relation]
|
|
100
|
+
# Access tokens relation
|
|
101
|
+
# @param application [Doorkeeper::Application]
|
|
102
|
+
# Application instance
|
|
103
|
+
# @param scopes [String, Doorkeeper::OAuth::Scopes]
|
|
104
|
+
# set of scopes
|
|
105
|
+
#
|
|
106
|
+
# @return [Doorkeeper::AccessToken, nil] Access Token instance or
|
|
107
|
+
# nil if matching record was not found
|
|
108
|
+
#
|
|
109
|
+
def find_matching_token(relation, application, scopes)
|
|
110
|
+
return nil unless relation
|
|
111
|
+
|
|
112
|
+
matching_tokens = []
|
|
113
|
+
|
|
114
|
+
find_access_token_in_batches(relation) do |batch|
|
|
115
|
+
tokens = batch.select do |token|
|
|
116
|
+
scopes_match?(token.scopes, scopes, application.try(:scopes))
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
matching_tokens.concat(tokens)
|
|
81
120
|
end
|
|
121
|
+
|
|
122
|
+
matching_tokens.max_by(&:created_at)
|
|
82
123
|
end
|
|
83
124
|
|
|
84
125
|
# Checks whether the token scopes match the scopes from the parameters
|
|
@@ -12,10 +12,10 @@ module Doorkeeper
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def issue_token
|
|
15
|
-
@token ||= AccessGrant.create!
|
|
15
|
+
@token ||= AccessGrant.create!(access_grant_attributes)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def
|
|
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
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
#
|
|
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?
|
|
@@ -5,11 +5,25 @@ module Doorkeeper
|
|
|
5
5
|
class ClientCredentialsRequest < BaseRequest
|
|
6
6
|
class Creator
|
|
7
7
|
def call(client, scopes, attributes = {})
|
|
8
|
+
existing_token = existing_token_for(client, scopes)
|
|
9
|
+
|
|
10
|
+
if Doorkeeper.configuration.reuse_access_token && existing_token&.reusable?
|
|
11
|
+
return existing_token
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
existing_token&.revoke
|
|
15
|
+
|
|
8
16
|
AccessToken.find_or_create_for(
|
|
9
17
|
client, nil, scopes, attributes[:expires_in],
|
|
10
18
|
attributes[:use_refresh_token]
|
|
11
19
|
)
|
|
12
20
|
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def existing_token_for(client, scopes)
|
|
25
|
+
Doorkeeper::AccessToken.matching_token_for client, nil, scopes
|
|
26
|
+
end
|
|
13
27
|
end
|
|
14
28
|
end
|
|
15
29
|
end
|
|
@@ -18,8 +18,8 @@ module Doorkeeper
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def redirect_uri
|
|
21
|
-
if URIChecker.
|
|
22
|
-
auth.
|
|
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,
|
|
@@ -25,10 +25,10 @@ module Doorkeeper
|
|
|
25
25
|
module Helpers
|
|
26
26
|
module URIChecker
|
|
27
27
|
def self.valid?(url)
|
|
28
|
-
return true if
|
|
28
|
+
return true if oob_uri?(url)
|
|
29
29
|
|
|
30
30
|
uri = as_uri(url)
|
|
31
|
-
uri
|
|
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.
|
|
82
|
-
|
|
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
|