doorkeeper 5.0.2 → 5.1.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 +5 -5
- data/.travis.yml +7 -3
- data/Dangerfile +5 -2
- data/Gemfile +3 -1
- data/NEWS.md +18 -10
- data/README.md +1 -1
- data/app/controllers/doorkeeper/tokens_controller.rb +6 -6
- data/app/views/doorkeeper/applications/show.html.erb +1 -1
- data/app/views/layouts/doorkeeper/admin.html.erb +5 -3
- data/bin/console +15 -0
- data/gemfiles/rails_4_2.gemfile +1 -0
- data/gemfiles/rails_5_0.gemfile +1 -0
- data/gemfiles/rails_5_1.gemfile +1 -0
- data/gemfiles/rails_5_2.gemfile +2 -1
- data/gemfiles/rails_master.gemfile +1 -0
- data/lib/doorkeeper/config.rb +73 -6
- data/lib/doorkeeper/helpers/controller.rb +3 -2
- data/lib/doorkeeper/models/access_grant_mixin.rb +8 -1
- data/lib/doorkeeper/models/access_token_mixin.rb +40 -9
- data/lib/doorkeeper/models/application_mixin.rb +52 -1
- data/lib/doorkeeper/models/concerns/hashable.rb +137 -0
- data/lib/doorkeeper/models/concerns/scopes.rb +1 -1
- data/lib/doorkeeper/oauth/authorization/code.rb +1 -1
- data/lib/doorkeeper/oauth/authorization/token.rb +1 -1
- data/lib/doorkeeper/oauth/authorization_code_request.rb +1 -1
- data/lib/doorkeeper/oauth/client.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +4 -3
- data/lib/doorkeeper/oauth/code_response.rb +2 -2
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +23 -8
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +32 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
- data/lib/doorkeeper/oauth/pre_authorization.rb +8 -3
- data/lib/doorkeeper/oauth/refresh_token_request.rb +4 -1
- data/lib/doorkeeper/oauth/token_response.rb +2 -2
- data/lib/doorkeeper/orm/active_record/access_grant.rb +22 -2
- data/lib/doorkeeper/orm/active_record/application.rb +16 -2
- data/lib/doorkeeper/version.rb +3 -3
- data/lib/doorkeeper.rb +1 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +41 -1
- data/spec/controllers/application_metal_controller_spec.rb +18 -4
- data/spec/controllers/tokens_controller_spec.rb +7 -11
- data/spec/dummy/app/controllers/application_controller.rb +1 -1
- data/spec/factories.rb +3 -3
- data/spec/lib/config_spec.rb +84 -0
- data/spec/lib/models/hashable_spec.rb +183 -0
- data/spec/lib/oauth/base_request_spec.rb +7 -7
- data/spec/lib/oauth/client_credentials/validation_spec.rb +3 -0
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +52 -17
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +20 -2
- data/spec/lib/oauth/password_access_token_request_spec.rb +32 -11
- data/spec/lib/oauth/pre_authorization_spec.rb +24 -0
- data/spec/lib/oauth/token_response_spec.rb +13 -13
- data/spec/lib/oauth/token_spec.rb +14 -0
- data/spec/models/doorkeeper/access_grant_spec.rb +61 -0
- data/spec/models/doorkeeper/access_token_spec.rb +123 -0
- data/spec/models/doorkeeper/application_spec.rb +37 -0
- data/spec/requests/flows/authorization_code_spec.rb +40 -0
- data/spec/requests/flows/password_spec.rb +4 -2
- data/spec/requests/flows/revoke_token_spec.rb +14 -30
- data/spec/spec_helper.rb +2 -1
- data/spec/support/ruby_2_6_rails_4_2_patch.rb +14 -0
- data/spec/support/shared/hashing_shared_context.rb +29 -0
- metadata +13 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5773ab2b97881fdf1bb6fa66a4e176299d58c536f354ea9e9f4f7bb6dc0dcdea
|
|
4
|
+
data.tar.gz: 69ac6937d7eb7786a8c9ac331f540ef5aa1321b7c0624a0778c7437151764569
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5e4b8470847ab8e642d50d27972838c0b5181b3c97c4a25afca18861b3878e513346f7b72d5c5d6c31b5d8513d90c1e6426e0cfcfce51a06b1e04e563ae09ab0
|
|
7
|
+
data.tar.gz: 79c4363faf9a3f41bc639ee6e6ec756efbd99a96e4670e788bc4201b6f69eb992ffc95f2f56e3efbcd5645f2ed76e2c8d29f6e9807ffb6841becd31d868c6963
|
data/.travis.yml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
cache: bundler
|
|
2
1
|
language: ruby
|
|
2
|
+
cache: bundler
|
|
3
3
|
sudo: false
|
|
4
4
|
|
|
5
5
|
rvm:
|
|
@@ -8,10 +8,12 @@ rvm:
|
|
|
8
8
|
- 2.3
|
|
9
9
|
- 2.4
|
|
10
10
|
- 2.5
|
|
11
|
-
-
|
|
11
|
+
- 2.6
|
|
12
|
+
- ruby-head
|
|
12
13
|
|
|
13
14
|
before_install:
|
|
14
|
-
-
|
|
15
|
+
- "find /home/travis/.rvm/rubies -wholename '*default/bundler-*.gemspec' -delete"
|
|
16
|
+
- rvm @global do gem uninstall bundler -a -x -I || true
|
|
15
17
|
- gem install bundler -v '~> 1.10'
|
|
16
18
|
|
|
17
19
|
gemfile:
|
|
@@ -41,5 +43,7 @@ matrix:
|
|
|
41
43
|
rvm: 2.2
|
|
42
44
|
- gemfile: gemfiles/rails_master.gemfile
|
|
43
45
|
rvm: 2.3
|
|
46
|
+
- gemfile: gemfiles/rails_master.gemfile
|
|
47
|
+
rvm: 2.4
|
|
44
48
|
allow_failures:
|
|
45
49
|
- gemfile: gemfiles/rails_master.gemfile
|
data/Dangerfile
CHANGED
|
@@ -17,7 +17,7 @@ end
|
|
|
17
17
|
# --------------------------------------------------------------------------------------------------------------------
|
|
18
18
|
# Has any changes happened inside the actual library code?
|
|
19
19
|
# --------------------------------------------------------------------------------------------------------------------
|
|
20
|
-
has_app_changes = !git.modified_files.grep(/lib/).empty?
|
|
20
|
+
has_app_changes = !git.modified_files.grep(/lib|app/).empty?
|
|
21
21
|
has_spec_changes = !git.modified_files.grep(/spec/).empty?
|
|
22
22
|
|
|
23
23
|
# --------------------------------------------------------------------------------------------------------------------
|
|
@@ -52,7 +52,10 @@ Here's an example of a #{CHANGELOG_FILE} entry:
|
|
|
52
52
|
```
|
|
53
53
|
MARKDOWN
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
warn(
|
|
56
|
+
"Please include a changelog entry. \nYou can find it at [#{CHANGELOG_FILE}](#{GITHUB_REPO}/blob/master/#{CHANGELOG_FILE})." +
|
|
57
|
+
"You can skip this warning only if you made some typo fix or other small changes that didn't affect the API."
|
|
58
|
+
)
|
|
56
59
|
end
|
|
57
60
|
|
|
58
61
|
if git.commits.any? { |commit| commit.message =~ /^Merge branch '#{github.branch_for_base}'/ }
|
data/Gemfile
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
source "https://rubygems.org"
|
|
2
2
|
|
|
3
|
-
gem "rails", "
|
|
3
|
+
gem "rails", ">= 5.2.1.1", "< 6.0"
|
|
4
4
|
|
|
5
5
|
gem "appraisal"
|
|
6
6
|
|
|
7
|
+
gem "bcrypt", "~> 3.1"
|
|
8
|
+
|
|
7
9
|
gem "activerecord-jdbcsqlite3-adapter", platform: :jruby
|
|
8
10
|
gem "sqlite3", platform: [:ruby, :mswin, :mingw, :x64_mingw]
|
|
9
11
|
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw]
|
data/NEWS.md
CHANGED
|
@@ -7,7 +7,15 @@ User-visible changes worth mentioning.
|
|
|
7
7
|
|
|
8
8
|
## master
|
|
9
9
|
|
|
10
|
-
- [#]
|
|
10
|
+
- [#1188] Use `params` instead of `request.POST` in tokens controller (fixes #1183).
|
|
11
|
+
- [#1179] Authorization Code Grant Flow without client id returns invalid_client error.
|
|
12
|
+
- [#1182] Fix loopback IP redirect URIs to conform with RFC8252, p. 7.3 (fixes #1170).
|
|
13
|
+
- [#1177] Allow to limit `scopes` for certain `grant_types`
|
|
14
|
+
- [#1162] Fix `enforce_content_type` for requests without body.
|
|
15
|
+
- [#1164] Fix error when `root_path` is not defined.
|
|
16
|
+
- [#1175] Internal refactor: use `scopes_string` inside `scopes`.
|
|
17
|
+
- [#1176] Fix test factory support for `factory_bot_rails`
|
|
18
|
+
- [#1168]: Allow optional hashing of tokens and secrets.
|
|
11
19
|
|
|
12
20
|
## 5.0.2
|
|
13
21
|
|
|
@@ -58,12 +66,12 @@ User-visible changes worth mentioning.
|
|
|
58
66
|
`Doorkeeper#installed?` method
|
|
59
67
|
- [#1031] Allow public clients to authenticate without `client_secret`. Define an app as
|
|
60
68
|
either public or private/confidential
|
|
61
|
-
|
|
69
|
+
|
|
62
70
|
**[IMPORTANT]**: all the applications (clients) now are considered as private by default.
|
|
63
71
|
You need to manually change `confidential` column to `false` if you are using public clients,
|
|
64
72
|
in other case your mobile (or other) applications will not be able to authorize.
|
|
65
73
|
See [#1142](https://github.com/doorkeeper-gem/doorkeeper/issues/1142) for more details.
|
|
66
|
-
|
|
74
|
+
|
|
67
75
|
- [#1010] Add configuration to enforce configured scopes (`default_scopes` and
|
|
68
76
|
`optional_scopes`) for applications
|
|
69
77
|
- [#1060] Ensure that the native redirect_uri parameter matches with redirect_uri of the client
|
|
@@ -81,26 +89,26 @@ User-visible changes worth mentioning.
|
|
|
81
89
|
- [#1076] Add config to enforce content type to application/x-www-form-urlencoded
|
|
82
90
|
- Fix bug with `force_ssl_in_redirect_uri` when it breaks existing applications with an
|
|
83
91
|
SSL redirect_uri.
|
|
84
|
-
|
|
92
|
+
|
|
85
93
|
## 4.4.3
|
|
86
|
-
|
|
94
|
+
|
|
87
95
|
- [#1143] Adds a config option `opt_out_native_route_change` to opt out of the breaking api
|
|
88
96
|
changed introduced in https://github.com/doorkeeper-gem/doorkeeper/pull/1003
|
|
89
97
|
|
|
90
|
-
|
|
98
|
+
|
|
91
99
|
## 4.4.2
|
|
92
100
|
|
|
93
101
|
- [#1130] Backport fix for native redirect_uri from 5.x.
|
|
94
|
-
|
|
102
|
+
|
|
95
103
|
## 4.4.1
|
|
96
104
|
|
|
97
105
|
- [#1127] Backport token type to comply with the RFC6750 specification.
|
|
98
106
|
- [#1125] Backport Quote surround I18n yes/no keys
|
|
99
|
-
|
|
107
|
+
|
|
100
108
|
## 4.4.0
|
|
101
|
-
|
|
109
|
+
|
|
102
110
|
- [#1120] Backport security fix from 5.x for token revocation when using public clients
|
|
103
|
-
|
|
111
|
+
|
|
104
112
|
**[IMPORTANT]**: all the applications (clients) now are considered as private by default.
|
|
105
113
|
You need to manually change `confidential` column to `false` if you are using public clients,
|
|
106
114
|
in other case your mobile (or other) applications will not be able to authorize.
|
data/README.md
CHANGED
|
@@ -4,11 +4,11 @@ module Doorkeeper
|
|
|
4
4
|
class TokensController < Doorkeeper::ApplicationMetalController
|
|
5
5
|
def create
|
|
6
6
|
response = authorize_response
|
|
7
|
-
headers.merge!
|
|
7
|
+
headers.merge!(response.headers)
|
|
8
8
|
self.response_body = response.body.to_json
|
|
9
9
|
self.status = response.status
|
|
10
|
-
rescue Errors::DoorkeeperError =>
|
|
11
|
-
handle_token_exception
|
|
10
|
+
rescue Errors::DoorkeeperError => error
|
|
11
|
+
handle_token_exception(error)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
# OAuth 2.0 Token Revocation - http://tools.ietf.org/html/rfc7009
|
|
@@ -75,12 +75,12 @@ module Doorkeeper
|
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
def token
|
|
78
|
-
@token ||= AccessToken.by_token(
|
|
79
|
-
AccessToken.by_refresh_token(
|
|
78
|
+
@token ||= AccessToken.by_token(params['token']) ||
|
|
79
|
+
AccessToken.by_refresh_token(params['token'])
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
def strategy
|
|
83
|
-
@strategy ||= server.token_request
|
|
83
|
+
@strategy ||= server.token_request(params[:grant_type])
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def authorize_response
|
|
@@ -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.
|
|
11
|
+
<p><code class="bg-light" id="secret"><%= @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>
|
|
@@ -17,9 +17,11 @@
|
|
|
17
17
|
<li class="nav-item <%= 'active' if request.path == oauth_applications_path %>">
|
|
18
18
|
<%= link_to t('doorkeeper.layouts.admin.nav.applications'), oauth_applications_path, class: 'nav-link' %>
|
|
19
19
|
</li>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
<% if respond_to?(:root_path) %>
|
|
21
|
+
<li class="nav-item">
|
|
22
|
+
<%= link_to t('doorkeeper.layouts.admin.nav.home'), root_path, class: 'nav-link' %>
|
|
23
|
+
</li>
|
|
24
|
+
<% end %>
|
|
23
25
|
</ul>
|
|
24
26
|
</div>
|
|
25
27
|
</nav>
|
data/bin/console
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
require 'rails/all'
|
|
5
|
+
require 'doorkeeper'
|
|
6
|
+
|
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
9
|
+
|
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
11
|
+
# require "pry"
|
|
12
|
+
# Pry.start
|
|
13
|
+
|
|
14
|
+
require 'irb'
|
|
15
|
+
IRB.start(__FILE__)
|
data/gemfiles/rails_4_2.gemfile
CHANGED
data/gemfiles/rails_5_0.gemfile
CHANGED
data/gemfiles/rails_5_1.gemfile
CHANGED
data/gemfiles/rails_5_2.gemfile
CHANGED
|
@@ -6,6 +6,7 @@ gem "rails", git: 'https://github.com/rails/rails'
|
|
|
6
6
|
gem "arel", git: 'https://github.com/rails/arel'
|
|
7
7
|
|
|
8
8
|
gem "appraisal"
|
|
9
|
+
gem "bcrypt", "~> 3.1"
|
|
9
10
|
gem "activerecord-jdbcsqlite3-adapter", platform: :jruby
|
|
10
11
|
gem "sqlite3", platform: [:ruby, :mswin, :mingw, :x64_mingw]
|
|
11
12
|
gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw]
|
data/lib/doorkeeper/config.rb
CHANGED
|
@@ -46,6 +46,7 @@ module Doorkeeper
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def build
|
|
49
|
+
@config.validate
|
|
49
50
|
@config
|
|
50
51
|
end
|
|
51
52
|
|
|
@@ -83,6 +84,13 @@ module Doorkeeper
|
|
|
83
84
|
@config.instance_variable_set(:@optional_scopes, OAuth::Scopes.from_array(scopes))
|
|
84
85
|
end
|
|
85
86
|
|
|
87
|
+
# Define scopes_by_grant_type to limit certain scope to certain grant_type
|
|
88
|
+
# @param { Hash } with grant_types as keys.
|
|
89
|
+
# Default set to {} i.e. no limitation on scopes usage
|
|
90
|
+
def scopes_by_grant_type(hash = {})
|
|
91
|
+
@config.instance_variable_set(:@scopes_by_grant_type, hash)
|
|
92
|
+
end
|
|
93
|
+
|
|
86
94
|
# Change the way client credentials are retrieved from the request object.
|
|
87
95
|
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
|
|
88
96
|
# falls back to the `:client_id` and `:client_secret` params from the
|
|
@@ -136,6 +144,24 @@ module Doorkeeper
|
|
|
136
144
|
def enforce_content_type
|
|
137
145
|
@config.instance_variable_set(:@enforce_content_type, true)
|
|
138
146
|
end
|
|
147
|
+
|
|
148
|
+
# Allow optional hashing of input tokens before persisting them.
|
|
149
|
+
# Will be used for hashing of input token and grants.
|
|
150
|
+
def hash_token_secrets
|
|
151
|
+
@config.instance_variable_set(:@hash_token_secrets, true)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Allow optional hashing of application secrets before persisting them.
|
|
155
|
+
# Will be used for hashing of input token and grants.
|
|
156
|
+
def hash_application_secrets
|
|
157
|
+
@config.instance_variable_set(:@hash_application_secrets, true)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Allow plain value lookup when using +hash_token_secrets+
|
|
161
|
+
# or +hash_application_secrets+ to avoid disrupting application experience
|
|
162
|
+
def fallback_to_plain_secrets
|
|
163
|
+
@config.instance_variable_set(:@fallback_to_plain_secrets, true)
|
|
164
|
+
end
|
|
139
165
|
end
|
|
140
166
|
|
|
141
167
|
module Option
|
|
@@ -286,9 +312,14 @@ module Doorkeeper
|
|
|
286
312
|
option :base_controller,
|
|
287
313
|
default: 'ActionController::Base'
|
|
288
314
|
|
|
289
|
-
attr_reader :
|
|
290
|
-
|
|
291
|
-
|
|
315
|
+
attr_reader :api_only,
|
|
316
|
+
:enforce_content_type,
|
|
317
|
+
:reuse_access_token
|
|
318
|
+
|
|
319
|
+
# Return the valid subset of this configuration
|
|
320
|
+
def validate
|
|
321
|
+
validate_reuse_access_token_value
|
|
322
|
+
end
|
|
292
323
|
|
|
293
324
|
def api_only
|
|
294
325
|
@api_only ||= false
|
|
@@ -307,21 +338,33 @@ module Doorkeeper
|
|
|
307
338
|
end
|
|
308
339
|
|
|
309
340
|
def enforce_configured_scopes?
|
|
310
|
-
|
|
341
|
+
option_set? :enforce_configured_scopes
|
|
311
342
|
end
|
|
312
343
|
|
|
313
344
|
def enable_application_owner?
|
|
314
|
-
|
|
345
|
+
option_set? :enable_application_owner
|
|
315
346
|
end
|
|
316
347
|
|
|
317
348
|
def confirm_application_owner?
|
|
318
|
-
|
|
349
|
+
option_set? :confirm_application_owner
|
|
319
350
|
end
|
|
320
351
|
|
|
321
352
|
def raise_on_errors?
|
|
322
353
|
handle_auth_errors == :raise
|
|
323
354
|
end
|
|
324
355
|
|
|
356
|
+
def hash_token_secrets?
|
|
357
|
+
option_set? :hash_token_secrets
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def hash_application_secrets?
|
|
361
|
+
option_set? :hash_application_secrets
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def fallback_to_plain_secrets?
|
|
365
|
+
option_set? :fallback_to_plain_secrets
|
|
366
|
+
end
|
|
367
|
+
|
|
325
368
|
def default_scopes
|
|
326
369
|
@default_scopes ||= OAuth::Scopes.new
|
|
327
370
|
end
|
|
@@ -334,6 +377,10 @@ module Doorkeeper
|
|
|
334
377
|
@scopes ||= default_scopes + optional_scopes
|
|
335
378
|
end
|
|
336
379
|
|
|
380
|
+
def scopes_by_grant_type
|
|
381
|
+
@scopes_by_grant_type ||= {}
|
|
382
|
+
end
|
|
383
|
+
|
|
337
384
|
def client_credentials_methods
|
|
338
385
|
@client_credentials_methods ||= %i[from_basic from_params]
|
|
339
386
|
end
|
|
@@ -352,6 +399,12 @@ module Doorkeeper
|
|
|
352
399
|
|
|
353
400
|
private
|
|
354
401
|
|
|
402
|
+
# Helper to read boolearized configuration option
|
|
403
|
+
def option_set?(instance_key)
|
|
404
|
+
var = instance_variable_get("@#{instance_key}")
|
|
405
|
+
!!(defined?(var) && var)
|
|
406
|
+
end
|
|
407
|
+
|
|
355
408
|
# Determines what values are acceptable for 'response_type' param in
|
|
356
409
|
# authorization request endpoint, and return them as an array of strings.
|
|
357
410
|
#
|
|
@@ -370,5 +423,19 @@ module Doorkeeper
|
|
|
370
423
|
types << 'refresh_token' if refresh_token_enabled?
|
|
371
424
|
types
|
|
372
425
|
end
|
|
426
|
+
|
|
427
|
+
# Determine whether +reuse_access_token+ and +hash_token_secrets+
|
|
428
|
+
# have both been activated.
|
|
429
|
+
#
|
|
430
|
+
# In that case, disable reuse_access_token value and warn the user.
|
|
431
|
+
def validate_reuse_access_token_value
|
|
432
|
+
return unless hash_token_secrets? && reuse_access_token
|
|
433
|
+
|
|
434
|
+
::Rails.logger.warn(
|
|
435
|
+
'You are configured both reuse_access_token AND hash_token_secrets. ' \
|
|
436
|
+
'This combination is unsupported. reuse_access_token will be disabled'
|
|
437
|
+
)
|
|
438
|
+
@reuse_access_token = false
|
|
439
|
+
end
|
|
373
440
|
end
|
|
374
441
|
end
|
|
@@ -55,8 +55,9 @@ module Doorkeeper
|
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def enforce_content_type
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
if (request.put? || request.post? || request.patch?) && request.content_type != 'application/x-www-form-urlencoded'
|
|
59
|
+
render json: {}, status: :unsupported_media_type
|
|
60
|
+
end
|
|
60
61
|
end
|
|
61
62
|
end
|
|
62
63
|
end
|
|
@@ -9,6 +9,7 @@ module Doorkeeper
|
|
|
9
9
|
include Models::Revocable
|
|
10
10
|
include Models::Accessible
|
|
11
11
|
include Models::Orderable
|
|
12
|
+
include Models::Hashable
|
|
12
13
|
include Models::Scopes
|
|
13
14
|
|
|
14
15
|
# never uses pkce, if pkce migrations were not generated
|
|
@@ -30,7 +31,13 @@ module Doorkeeper
|
|
|
30
31
|
# if there is no record with such token
|
|
31
32
|
#
|
|
32
33
|
def by_token(token)
|
|
33
|
-
|
|
34
|
+
find_by_plaintext_token(:token, token)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# We want to perform secret hashing whenever the user
|
|
38
|
+
# enables the configuration option +hash_token_secrets+
|
|
39
|
+
def perform_secret_hashing?
|
|
40
|
+
Doorkeeper.configuration.hash_token_secrets?
|
|
34
41
|
end
|
|
35
42
|
|
|
36
43
|
# Revokes AccessGrant records that have not been revoked and associated
|
|
@@ -9,20 +9,21 @@ module Doorkeeper
|
|
|
9
9
|
include Models::Revocable
|
|
10
10
|
include Models::Accessible
|
|
11
11
|
include Models::Orderable
|
|
12
|
+
include Models::Hashable
|
|
12
13
|
include Models::Scopes
|
|
13
14
|
|
|
14
15
|
module ClassMethods
|
|
15
16
|
# Returns an instance of the Doorkeeper::AccessToken with
|
|
16
|
-
# specific token value.
|
|
17
|
+
# specific plain text token value.
|
|
17
18
|
#
|
|
18
19
|
# @param token [#to_s]
|
|
19
|
-
# token value (any object that responds to `#to_s`)
|
|
20
|
+
# Plain text token value (any object that responds to `#to_s`)
|
|
20
21
|
#
|
|
21
22
|
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
|
|
22
23
|
# if there is no record with such token
|
|
23
24
|
#
|
|
24
25
|
def by_token(token)
|
|
25
|
-
|
|
26
|
+
find_by_plaintext_token(:token, token)
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
# Returns an instance of the Doorkeeper::AccessToken
|
|
@@ -35,7 +36,13 @@ module Doorkeeper
|
|
|
35
36
|
# if there is no record with such refresh token
|
|
36
37
|
#
|
|
37
38
|
def by_refresh_token(refresh_token)
|
|
38
|
-
|
|
39
|
+
find_by_plaintext_token(:refresh_token, refresh_token)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# We want to perform secret hashing whenever the user
|
|
43
|
+
# enables the configuration option +hash_token_secrets+
|
|
44
|
+
def perform_secret_hashing?
|
|
45
|
+
Doorkeeper.configuration.hash_token_secrets?
|
|
39
46
|
end
|
|
40
47
|
|
|
41
48
|
# Revokes AccessToken records that have not been revoked and associated
|
|
@@ -98,9 +105,9 @@ module Doorkeeper
|
|
|
98
105
|
|
|
99
106
|
(token_scopes.sort == param_scopes.sort) &&
|
|
100
107
|
Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
|
|
101
|
-
param_scopes.to_s,
|
|
102
|
-
Doorkeeper.configuration.scopes,
|
|
103
|
-
app_scopes
|
|
108
|
+
scope_str: param_scopes.to_s,
|
|
109
|
+
server_scopes: Doorkeeper.configuration.scopes,
|
|
110
|
+
app_scopes: app_scopes
|
|
104
111
|
)
|
|
105
112
|
end
|
|
106
113
|
|
|
@@ -219,6 +226,26 @@ module Doorkeeper
|
|
|
219
226
|
accessible? && includes_scope?(*scopes)
|
|
220
227
|
end
|
|
221
228
|
|
|
229
|
+
# We keep a volatile copy of the raw refresh token for initial communication
|
|
230
|
+
# The stored refresh_token may be mapped and not available in cleartext.
|
|
231
|
+
def plaintext_refresh_token
|
|
232
|
+
if perform_secret_hashing?
|
|
233
|
+
@raw_refresh_token
|
|
234
|
+
else
|
|
235
|
+
refresh_token
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# We keep a volatile copy of the raw token for initial communication
|
|
240
|
+
# The stored refresh_token may be mapped and not available in cleartext.
|
|
241
|
+
def plaintext_token
|
|
242
|
+
if perform_secret_hashing?
|
|
243
|
+
@raw_token
|
|
244
|
+
else
|
|
245
|
+
token
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
222
249
|
private
|
|
223
250
|
|
|
224
251
|
# Generates refresh token with UniqueToken generator.
|
|
@@ -226,7 +253,8 @@ module Doorkeeper
|
|
|
226
253
|
# @return [String] refresh token value
|
|
227
254
|
#
|
|
228
255
|
def generate_refresh_token
|
|
229
|
-
|
|
256
|
+
@raw_refresh_token = UniqueToken.generate
|
|
257
|
+
self.refresh_token = hashed_or_plain_token(@raw_refresh_token)
|
|
230
258
|
end
|
|
231
259
|
|
|
232
260
|
# Generates and sets the token value with the
|
|
@@ -242,13 +270,16 @@ module Doorkeeper
|
|
|
242
270
|
def generate_token
|
|
243
271
|
self.created_at ||= Time.now.utc
|
|
244
272
|
|
|
245
|
-
|
|
273
|
+
@raw_token = token_generator.generate(
|
|
246
274
|
resource_owner_id: resource_owner_id,
|
|
247
275
|
scopes: scopes,
|
|
248
276
|
application: application,
|
|
249
277
|
expires_in: expires_in,
|
|
250
278
|
created_at: created_at
|
|
251
279
|
)
|
|
280
|
+
|
|
281
|
+
self.token = hashed_or_plain_token(@raw_token)
|
|
282
|
+
@raw_token
|
|
252
283
|
end
|
|
253
284
|
|
|
254
285
|
def token_generator
|
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'bcrypt'
|
|
4
|
+
|
|
3
5
|
module Doorkeeper
|
|
4
6
|
module ApplicationMixin
|
|
5
7
|
extend ActiveSupport::Concern
|
|
6
8
|
|
|
7
9
|
include OAuth::Helpers
|
|
8
10
|
include Models::Orderable
|
|
11
|
+
include Models::Hashable
|
|
9
12
|
include Models::Scopes
|
|
10
13
|
|
|
14
|
+
included do
|
|
15
|
+
# Use BCrypt as the hashing function for applications
|
|
16
|
+
self.secret_hash_function = lambda do |plain_token|
|
|
17
|
+
BCrypt::Password.create(plain_token.to_s)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Also need to override the comparer function for BCrypt
|
|
21
|
+
self.secret_comparer = lambda do |plain, secret|
|
|
22
|
+
begin
|
|
23
|
+
BCrypt::Password.new(secret.to_s) == plain.to_s
|
|
24
|
+
rescue BCrypt::Errors::InvalidHash
|
|
25
|
+
false
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# :nodoc
|
|
11
31
|
module ClassMethods
|
|
32
|
+
# We want to perform secret hashing whenever the user
|
|
33
|
+
# enables the configuration option +hash_application_secrets+
|
|
34
|
+
def perform_secret_hashing?
|
|
35
|
+
Doorkeeper.configuration.hash_application_secrets?
|
|
36
|
+
end
|
|
37
|
+
|
|
12
38
|
# Returns an instance of the Doorkeeper::Application with
|
|
13
39
|
# specific UID and secret.
|
|
14
40
|
#
|
|
@@ -25,7 +51,7 @@ module Doorkeeper
|
|
|
25
51
|
app = by_uid(uid)
|
|
26
52
|
return unless app
|
|
27
53
|
return app if secret.blank? && !app.confidential?
|
|
28
|
-
return unless app.secret
|
|
54
|
+
return unless app.secret_matches?(secret)
|
|
29
55
|
app
|
|
30
56
|
end
|
|
31
57
|
|
|
@@ -49,5 +75,30 @@ module Doorkeeper
|
|
|
49
75
|
def redirect_uri=(uris)
|
|
50
76
|
super(uris.is_a?(Array) ? uris.join("\n") : uris)
|
|
51
77
|
end
|
|
78
|
+
|
|
79
|
+
# Check whether the given plain text secret matches our stored secret
|
|
80
|
+
#
|
|
81
|
+
# @param input [#to_s] Plain secret provided by user
|
|
82
|
+
# (any object that responds to `#to_s`)
|
|
83
|
+
#
|
|
84
|
+
# @return [true] Whether the given secret matches the stored secret
|
|
85
|
+
# of this application.
|
|
86
|
+
#
|
|
87
|
+
def secret_matches?(input)
|
|
88
|
+
# return false if either is nil, since secure_compare depends on strings
|
|
89
|
+
# but Application secrets MAY be nil depending on confidentiality.
|
|
90
|
+
return false if input.nil? || secret.nil?
|
|
91
|
+
|
|
92
|
+
# When matching the secret by comparer function, all is well.
|
|
93
|
+
return true if self.class.secret_matches?(input, secret)
|
|
94
|
+
|
|
95
|
+
# When fallback lookup is enabled, ensure applications
|
|
96
|
+
# with plain secrets can still be found
|
|
97
|
+
if Doorkeeper.configuration.fallback_to_plain_secrets?
|
|
98
|
+
ActiveSupport::SecurityUtils.secure_compare(input, secret)
|
|
99
|
+
else
|
|
100
|
+
false
|
|
101
|
+
end
|
|
102
|
+
end
|
|
52
103
|
end
|
|
53
104
|
end
|