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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +812 -0
- data/CONTRIBUTING.md +4 -9
- data/Dangerfile +1 -1
- 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/tokens_controller.rb +18 -8
- 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 +30 -3
- data/lib/doorkeeper/grape/helpers.rb +5 -1
- data/lib/doorkeeper/helpers/controller.rb +16 -3
- data/lib/doorkeeper/oauth/authorization/code.rb +10 -8
- data/lib/doorkeeper/oauth/authorization/token.rb +1 -1
- 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 +13 -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 +41 -9
- data/lib/generators/doorkeeper/templates/migration.rb.erb +3 -0
- data/spec/controllers/applications_controller_spec.rb +93 -0
- data/spec/controllers/tokens_controller_spec.rb +71 -3
- data/spec/dummy/config/application.rb +3 -1
- data/spec/dummy/config/initializers/doorkeeper.rb +27 -9
- data/spec/lib/config_spec.rb +11 -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 +6 -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) }
|
|
@@ -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
|
|
@@ -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
|
-
|
|
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,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) ||
|
|
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
|
|
@@ -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?
|
|
@@ -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
|
|
@@ -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
|
|
19
|
-
@refresh_token
|
|
20
|
-
@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? &&
|
|
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
|
-
#
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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.
|