doorkeeper 5.2.0.rc1 → 5.2.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of doorkeeper might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/CONTRIBUTING.md +7 -0
- data/Dangerfile +1 -1
- data/Dockerfile +29 -0
- data/app/controllers/doorkeeper/authorizations_controller.rb +6 -1
- data/app/controllers/doorkeeper/tokens_controller.rb +14 -1
- data/lib/doorkeeper/config.rb +9 -6
- data/lib/doorkeeper/models/access_token_mixin.rb +43 -2
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +14 -0
- data/lib/doorkeeper/oauth/token_introspection.rb +12 -6
- data/lib/doorkeeper/version.rb +1 -1
- data/lib/generators/doorkeeper/templates/initializer.rb +35 -22
- data/spec/controllers/authorizations_controller_spec.rb +13 -0
- data/spec/controllers/tokens_controller_spec.rb +140 -40
- data/spec/dummy/config/initializers/doorkeeper.rb +47 -20
- data/spec/lib/oauth/client_credentials/creator_spec.rb +3 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 30363aebde1b255fb64b8df11e5d9b1c80f0d896ebb970908f764a8a5fd17fdf
|
4
|
+
data.tar.gz: 129cddd18b7ea164da1387d8a3fef5c40b4be96eca9f141ebb0167a318f28a07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 48f707ac0ee598c6987cf396cc168f57975abe8fbf78ce0479733e1b7fdf35ef6abcd73474f0141c72eee1b08ce8a9f0cf46d866f974c0feea8ae9609cb36d07
|
7
|
+
data.tar.gz: 79c4aa3fdae50be283f3ef78c544554ad5d50d8395d6f6970a41c73a0d46d427b859dd07e38685120d0df9b1016567a3a34b79d882b9b23d6bb470a19c95ecd8
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,19 @@ User-visible changes worth mentioning.
|
|
7
7
|
|
8
8
|
## master
|
9
9
|
|
10
|
+
- [#PR ID] Add your description here
|
11
|
+
|
12
|
+
## 5.2.0.rc2
|
13
|
+
|
14
|
+
- [#1270] Find matching tokens in batches for `reuse_access_token` option (fix #1193).
|
15
|
+
- [#1271] Reintroduce existing token revocation for client credentials.
|
16
|
+
- [#1269] Update initializer template documentation.
|
17
|
+
- [#1266] Use strong parameters within pre-authorization.
|
18
|
+
- [#1264] Add :before_successful_authorization and :after_successful_authorization hooks in TokensController
|
19
|
+
- [#1263] Response properly when introspection fails and fix configurations's user guide.
|
20
|
+
|
21
|
+
## 5.2.0.rc1
|
22
|
+
|
10
23
|
- [#1260], [#1261] Improve Token Introspection configuration option (access to tokens, client).
|
11
24
|
- [#1257] Add constraint configuration when using client authentication on introspection endpoint.
|
12
25
|
- [#1252] Returning `unauthorized` when the revocation of the token should not be performed due to wrong permissions.
|
@@ -31,7 +44,7 @@ User-visible changes worth mentioning.
|
|
31
44
|
|
32
45
|
- [#1208] Unify hashing implementation into secret storing strategies
|
33
46
|
|
34
|
-
**[IMPORTANT]
|
47
|
+
**[IMPORTANT]** If you have been using the master branch of doorkeeper with bcrypt in your Gemfile.lock,
|
35
48
|
your application secrets have been hashed using BCrypt. To restore this behavior, use the initializer option
|
36
49
|
`use_application_hashing using: 'Doorkeeper::SecretStoring::BCrypt`.
|
37
50
|
|
data/CONTRIBUTING.md
CHANGED
@@ -7,6 +7,13 @@ Fork, then clone the repo:
|
|
7
7
|
|
8
8
|
git clone git@github.com:your-username/doorkeeper.git
|
9
9
|
|
10
|
+
### Docker Setup
|
11
|
+
|
12
|
+
Build the container image with: `docker build --pull -t doorkeeper:test .`
|
13
|
+
Run the tests with: `docker run -it --rm doorkeeper:test`
|
14
|
+
|
15
|
+
### Local Setup
|
16
|
+
|
10
17
|
Set up Ruby dependencies via Bundler
|
11
18
|
|
12
19
|
bundle install
|
data/Dangerfile
CHANGED
@@ -11,7 +11,7 @@ def changelog_entry_example
|
|
11
11
|
.sub(/[?.!,;]?$/, '')
|
12
12
|
.capitalize
|
13
13
|
|
14
|
-
"- [##{pr_number}]
|
14
|
+
"- [##{pr_number}] #{pr_title}."
|
15
15
|
end
|
16
16
|
|
17
17
|
# --------------------------------------------------------------------------------------------------------------------
|
data/Dockerfile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
FROM ruby:2.6.3-alpine3.9
|
2
|
+
|
3
|
+
RUN apk add --no-cache \
|
4
|
+
ca-certificates \
|
5
|
+
wget \
|
6
|
+
openssl \
|
7
|
+
bash \
|
8
|
+
build-base \
|
9
|
+
git \
|
10
|
+
sqlite-dev \
|
11
|
+
tzdata
|
12
|
+
|
13
|
+
ENV LANG en_US.UTF-8
|
14
|
+
ENV LANGUAGE en_US:en
|
15
|
+
ENV LC_ALL en_US.UTF-8
|
16
|
+
|
17
|
+
ENV BUNDLER_VERSION 2.0.1
|
18
|
+
RUN gem install bundler -v ${BUNDLER_VERSION} -i /usr/local/lib/ruby/gems/$(ls /usr/local/lib/ruby/gems) --force
|
19
|
+
|
20
|
+
WORKDIR /srv
|
21
|
+
|
22
|
+
COPY Gemfile doorkeeper.gemspec /srv/
|
23
|
+
COPY lib/doorkeeper/version.rb /srv/lib/doorkeeper/version.rb
|
24
|
+
|
25
|
+
RUN bundle install
|
26
|
+
|
27
|
+
COPY . /srv/
|
28
|
+
|
29
|
+
CMD ["rake"]
|
@@ -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
|
@@ -88,7 +88,20 @@ module Doorkeeper
|
|
88
88
|
end
|
89
89
|
|
90
90
|
def authorize_response
|
91
|
-
@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)
|
92
105
|
end
|
93
106
|
|
94
107
|
def revocation_error_response
|
data/lib/doorkeeper/config.rb
CHANGED
@@ -322,9 +322,10 @@ module Doorkeeper
|
|
322
322
|
end)
|
323
323
|
|
324
324
|
# Configure protection of token introspection request.
|
325
|
-
# By default
|
326
|
-
#
|
327
|
-
#
|
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)
|
328
329
|
#
|
329
330
|
# You can define any custom rule you need or just disable token
|
330
331
|
# introspection at all.
|
@@ -340,9 +341,11 @@ module Doorkeeper
|
|
340
341
|
# Bearer token used to authorize the request
|
341
342
|
#
|
342
343
|
option :allow_token_introspection,
|
343
|
-
default: (lambda do |token, authorized_client,
|
344
|
-
if
|
345
|
-
|
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
|
346
349
|
else
|
347
350
|
true
|
348
351
|
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
|
@@ -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
|
@@ -67,7 +67,7 @@ module Doorkeeper
|
|
67
67
|
# HTTP 401 code as described in Section 3 of OAuth 2.0 Bearer Token
|
68
68
|
# Usage [RFC6750].
|
69
69
|
#
|
70
|
-
@error = :invalid_token
|
70
|
+
@error = :invalid_token unless valid_authorized_token?
|
71
71
|
else
|
72
72
|
@error = :invalid_request
|
73
73
|
end
|
@@ -149,9 +149,9 @@ module Doorkeeper
|
|
149
149
|
#
|
150
150
|
def active?
|
151
151
|
if authorized_client
|
152
|
-
valid_token? && token_introspection_allowed?(authorized_client.application)
|
152
|
+
valid_token? && token_introspection_allowed?(auth_client: authorized_client.application)
|
153
153
|
else
|
154
|
-
valid_token?
|
154
|
+
valid_token?
|
155
155
|
end
|
156
156
|
end
|
157
157
|
|
@@ -160,20 +160,26 @@ module Doorkeeper
|
|
160
160
|
@token&.accessible?
|
161
161
|
end
|
162
162
|
|
163
|
+
def valid_authorized_token?
|
164
|
+
!authorized_token_matches_introspected? &&
|
165
|
+
authorized_token.accessible? &&
|
166
|
+
token_introspection_allowed?(auth_token: authorized_token)
|
167
|
+
end
|
168
|
+
|
163
169
|
# RFC7662 Section 2.1
|
164
170
|
def authorized_token_matches_introspected?
|
165
171
|
authorized_token.token == @token&.token
|
166
172
|
end
|
167
173
|
|
168
174
|
# config constraints for introspection in Doorkeeper.configuration.allow_token_introspection
|
169
|
-
def token_introspection_allowed?(
|
175
|
+
def token_introspection_allowed?(auth_client: nil, auth_token: nil)
|
170
176
|
allow_introspection = Doorkeeper.configuration.allow_token_introspection
|
171
177
|
return allow_introspection unless allow_introspection.respond_to?(:call)
|
172
178
|
|
173
179
|
allow_introspection.call(
|
174
180
|
@token,
|
175
|
-
|
176
|
-
|
181
|
+
auth_client,
|
182
|
+
auth_token
|
177
183
|
)
|
178
184
|
end
|
179
185
|
|
data/lib/doorkeeper/version.rb
CHANGED
@@ -65,7 +65,7 @@ Doorkeeper.configure do
|
|
65
65
|
# end
|
66
66
|
|
67
67
|
# Use a custom class for generating the access token.
|
68
|
-
# See https://
|
68
|
+
# See https://doorkeeper.gitbook.io/guides/configuration/other-configurations#custom-access-token-generator
|
69
69
|
#
|
70
70
|
# access_token_generator '::Doorkeeper::JWT'
|
71
71
|
|
@@ -160,7 +160,7 @@ Doorkeeper.configure do
|
|
160
160
|
|
161
161
|
# Define access token scopes for your provider
|
162
162
|
# For more information go to
|
163
|
-
# https://
|
163
|
+
# https://doorkeeper.gitbook.io/guides/ruby-on-rails/scopes
|
164
164
|
#
|
165
165
|
# default_scopes :public
|
166
166
|
# optional_scopes :write, :update
|
@@ -296,7 +296,7 @@ Doorkeeper.configure do
|
|
296
296
|
# or add any other functionality.
|
297
297
|
#
|
298
298
|
# before_successful_authorization do |controller|
|
299
|
-
# Rails.logger.info(params.inspect)
|
299
|
+
# Rails.logger.info(controller.request.params.inspect)
|
300
300
|
# end
|
301
301
|
#
|
302
302
|
# after_successful_authorization do |controller|
|
@@ -314,23 +314,13 @@ Doorkeeper.configure do
|
|
314
314
|
# client.superapp? or resource_owner.admin?
|
315
315
|
# end
|
316
316
|
|
317
|
-
# Configure custom constraints for the Token Introspection request.
|
318
|
-
# this configuration option allows to introspect
|
319
|
-
#
|
317
|
+
# Configure custom constraints for the Token Introspection request.
|
318
|
+
# By default this configuration option allows to introspect a token by another
|
319
|
+
# token of the same application, OR to introspect the token that belongs to
|
320
|
+
# authorized client (from authenticated client) OR when token doesn't
|
320
321
|
# belong to any client (public token). Otherwise requester has no access to the
|
321
322
|
# introspection and it will return response as stated in the RFC.
|
322
323
|
#
|
323
|
-
# You can define your custom check:
|
324
|
-
#
|
325
|
-
# allow_token_introspection do |token, authorized_client, _authorized_token|
|
326
|
-
# if token.application
|
327
|
-
# # `protected_resource` is a new database boolean column, for example
|
328
|
-
# token.application == authorized_client || authorized_client.protected_resource?
|
329
|
-
# else
|
330
|
-
# true
|
331
|
-
# end
|
332
|
-
# end
|
333
|
-
#
|
334
324
|
# Block arguments:
|
335
325
|
#
|
336
326
|
# @param token [Doorkeeper::AccessToken]
|
@@ -343,15 +333,38 @@ Doorkeeper.configure do
|
|
343
333
|
# @param authorized_token [Doorkeeper::AccessToken]
|
344
334
|
# Bearer token used to authorize the request
|
345
335
|
#
|
346
|
-
#
|
347
|
-
#
|
348
|
-
#
|
336
|
+
# In case the block returns `nil` or `false` introspection responses with 401 status code
|
337
|
+
# when using authorized token to introspect, or you'll get 200 with { "active": false } body
|
338
|
+
# when using authorized client to introspect as stated in the
|
339
|
+
# RFC 7662 section 2.2. Introspection Response.
|
340
|
+
#
|
341
|
+
# Using with caution:
|
342
|
+
# Keep in mind that these three parameters pass to block can be nil as following case:
|
343
|
+
# `authorized_client` is nil if and only if `authorized_token` is present, and vice versa.
|
344
|
+
# `token` will be nil if and only if `authorized_token` is present.
|
345
|
+
# So remember to use `&` or check if it is present before calling method on
|
346
|
+
# them to make sure you doesn't get NoMethodError exception.
|
347
|
+
#
|
348
|
+
# You can define your custom check:
|
349
|
+
#
|
350
|
+
# allow_token_introspection do |token, authorized_client, authorized_token|
|
351
|
+
# if authorized_token
|
352
|
+
# # customize: require `introspection` scope
|
353
|
+
# authorized_token.application == token&.application ||
|
354
|
+
# authorized_token.scopes.include?("introspection")
|
355
|
+
# elsif token.application
|
356
|
+
# # `protected_resource` is a new database boolean column, for example
|
357
|
+
# authorized_client == token.application || authorized_client.protected_resource?
|
358
|
+
# else
|
359
|
+
# # public token (when token.application is nil, token doesn't belong to any application)
|
360
|
+
# true
|
361
|
+
# end
|
362
|
+
# end
|
349
363
|
#
|
350
|
-
#
|
364
|
+
# Or you can completely disable any token introspection:
|
351
365
|
#
|
352
366
|
# allow_token_introspection false
|
353
367
|
#
|
354
|
-
# In such case every request for token introspection will get { "active": false } response.
|
355
368
|
# If you need to block the request at all, then configure your routes.rb or web-server
|
356
369
|
# like nginx to forbid the request.
|
357
370
|
|
@@ -524,4 +524,17 @@ describe Doorkeeper::AuthorizationsController, "implicit grant flow" do
|
|
524
524
|
post :create
|
525
525
|
end
|
526
526
|
end
|
527
|
+
|
528
|
+
describe "strong parameters" do
|
529
|
+
it "ignores non-scalar scope parameter" do
|
530
|
+
get :new, params: {
|
531
|
+
client_id: client.uid,
|
532
|
+
response_type: "token",
|
533
|
+
redirect_uri: client.redirect_uri,
|
534
|
+
scope: { "0" => "profile" },
|
535
|
+
}
|
536
|
+
|
537
|
+
expect(response).to be_successful
|
538
|
+
end
|
539
|
+
end
|
527
540
|
end
|
@@ -3,31 +3,139 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
5
|
describe Doorkeeper::TokensController do
|
6
|
-
|
7
|
-
|
6
|
+
let(:client) { FactoryBot.create :application }
|
7
|
+
let!(:user) { User.create!(name: "Joe", password: "sekret") }
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
before do
|
10
|
+
Doorkeeper.configure do
|
11
|
+
resource_owner_from_credentials do
|
12
|
+
User.first
|
13
|
+
end
|
14
|
+
end
|
11
15
|
|
12
|
-
|
16
|
+
allow(Doorkeeper.configuration).to receive(:grant_flows).and_return(["password"])
|
17
|
+
end
|
13
18
|
|
14
|
-
|
19
|
+
subject { JSON.parse(response.body) }
|
20
|
+
|
21
|
+
describe "POST #create" do
|
22
|
+
before do
|
23
|
+
post :create, params: {
|
24
|
+
client_id: client.uid,
|
25
|
+
client_secret: client.secret,
|
26
|
+
grant_type: "password",
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
it "responds after authorization" do
|
31
|
+
expect(response).to be_successful
|
32
|
+
end
|
33
|
+
|
34
|
+
it "includes access token in response" do
|
35
|
+
expect(subject["access_token"]).to eq(Doorkeeper::AccessToken.first.token)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "includes token type in response" do
|
39
|
+
expect(subject["token_type"]).to eq("Bearer")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "includes token expiration in response" do
|
43
|
+
expect(subject["expires_in"].to_i).to eq(Doorkeeper.configuration.access_token_expires_in)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "issues the token for the current client" do
|
47
|
+
expect(Doorkeeper::AccessToken.first.application_id).to eq(client.id)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "issues the token for the current resource owner" do
|
51
|
+
expect(Doorkeeper::AccessToken.first.resource_owner_id).to eq(user.id)
|
15
52
|
end
|
16
53
|
end
|
17
54
|
|
18
|
-
describe "
|
19
|
-
|
20
|
-
|
21
|
-
|
55
|
+
describe "POST #create with errors" do
|
56
|
+
before do
|
57
|
+
post :create, params: {
|
58
|
+
client_id: client.uid,
|
59
|
+
client_secret: "invalid",
|
60
|
+
grant_type: "password",
|
61
|
+
}
|
62
|
+
end
|
22
63
|
|
23
|
-
|
64
|
+
it "responds after authorization" do
|
65
|
+
expect(response).to be_unauthorized
|
66
|
+
end
|
24
67
|
|
25
|
-
|
26
|
-
expect(
|
68
|
+
it "include error in response" do
|
69
|
+
expect(subject["error"]).to eq("invalid_client")
|
70
|
+
end
|
71
|
+
|
72
|
+
it "include error_description in response" do
|
73
|
+
expect(subject["error_description"]).to be
|
74
|
+
end
|
75
|
+
|
76
|
+
it "does not include access token in response" do
|
77
|
+
expect(subject["access_token"]).to be_nil
|
78
|
+
end
|
79
|
+
|
80
|
+
it "does not include token type in response" do
|
81
|
+
expect(subject["token_type"]).to be_nil
|
82
|
+
end
|
83
|
+
|
84
|
+
it "does not include token expiration in response" do
|
85
|
+
expect(subject["expires_in"]).to be_nil
|
86
|
+
end
|
87
|
+
|
88
|
+
it "does not issue any access token" do
|
89
|
+
expect(Doorkeeper::AccessToken.all).to be_empty
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "POST #create with callbacks" do
|
94
|
+
after do
|
95
|
+
client.update_attribute :redirect_uri, "urn:ietf:wg:oauth:2.0:oob"
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "when successful" do
|
99
|
+
after do
|
100
|
+
post :create, params: {
|
101
|
+
client_id: client.uid,
|
102
|
+
client_secret: client.secret,
|
103
|
+
grant_type: "password",
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should call :before_successful_authorization callback" do
|
108
|
+
expect(Doorkeeper.configuration)
|
109
|
+
.to receive_message_chain(:before_successful_authorization, :call).with(instance_of(described_class))
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should call :after_successful_authorization callback" do
|
113
|
+
expect(Doorkeeper.configuration)
|
114
|
+
.to receive_message_chain(:after_successful_authorization, :call).with(instance_of(described_class))
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "with errors" do
|
119
|
+
after do
|
120
|
+
post :create, params: {
|
121
|
+
client_id: client.uid,
|
122
|
+
client_secret: "invalid",
|
123
|
+
grant_type: "password",
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should call :before_successful_authorization callback" do
|
128
|
+
expect(Doorkeeper.configuration)
|
129
|
+
.to receive_message_chain(:before_successful_authorization, :call).with(instance_of(described_class))
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should not call :after_successful_authorization callback" do
|
133
|
+
expect(Doorkeeper.configuration).not_to receive(:after_successful_authorization)
|
134
|
+
end
|
27
135
|
end
|
28
136
|
end
|
29
137
|
|
30
|
-
describe "
|
138
|
+
describe "POST #create with custom error" do
|
31
139
|
it "returns the error response with a custom message" do
|
32
140
|
# I18n looks for `doorkeeper.errors.messages.custom_message` in locale files
|
33
141
|
custom_message = "my_message"
|
@@ -58,7 +166,7 @@ describe Doorkeeper::TokensController do
|
|
58
166
|
end
|
59
167
|
|
60
168
|
# http://tools.ietf.org/html/rfc7009#section-2.2
|
61
|
-
describe "
|
169
|
+
describe "POST #revoke" do
|
62
170
|
let(:client) { FactoryBot.create(:application) }
|
63
171
|
let(:access_token) { FactoryBot.create(:access_token, application: client) }
|
64
172
|
|
@@ -117,21 +225,7 @@ describe Doorkeeper::TokensController do
|
|
117
225
|
end
|
118
226
|
end
|
119
227
|
|
120
|
-
describe "
|
121
|
-
it "memoizes the result of the authorization" do
|
122
|
-
strategy = double(:strategy, authorize: true)
|
123
|
-
expect(strategy).to receive(:authorize).once
|
124
|
-
allow(controller).to receive(:strategy) { strategy }
|
125
|
-
allow(controller).to receive(:create) do
|
126
|
-
2.times { controller.send :authorize_response }
|
127
|
-
controller.render json: {}, status: :ok
|
128
|
-
end
|
129
|
-
|
130
|
-
post :create
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
describe "when requested token introspection" do
|
228
|
+
describe "POST #introspect" do
|
135
229
|
let(:client) { FactoryBot.create(:application) }
|
136
230
|
let(:access_token) { FactoryBot.create(:access_token, application: client) }
|
137
231
|
let(:token_for_introspection) { FactoryBot.create(:access_token, application: client) }
|
@@ -167,13 +261,15 @@ describe Doorkeeper::TokensController do
|
|
167
261
|
end
|
168
262
|
end
|
169
263
|
|
170
|
-
it "responds with
|
264
|
+
it "responds with invalid_token error" do
|
171
265
|
request.headers["Authorization"] = "Bearer #{access_token.token}"
|
172
266
|
|
173
267
|
post :introspect, params: { token: token_for_introspection.token }
|
174
268
|
|
175
|
-
|
176
|
-
|
269
|
+
response_status_should_be 401
|
270
|
+
|
271
|
+
should_not_have_json "active"
|
272
|
+
should_have_json "error", "invalid_token"
|
177
273
|
end
|
178
274
|
end
|
179
275
|
|
@@ -268,15 +364,17 @@ describe Doorkeeper::TokensController do
|
|
268
364
|
expect(json_response).to include("client_id", "token_type", "exp", "iat")
|
269
365
|
end
|
270
366
|
|
271
|
-
it "responds with
|
367
|
+
it "responds with invalid_token error if authorized token doesn't have introspection scope" do
|
272
368
|
access_token.update(scopes: "read write")
|
273
369
|
|
274
370
|
request.headers["Authorization"] = "Bearer #{access_token.token}"
|
275
371
|
|
276
372
|
post :introspect, params: { token: token_for_introspection.token }
|
277
373
|
|
278
|
-
|
279
|
-
|
374
|
+
response_status_should_be 401
|
375
|
+
|
376
|
+
should_not_have_json "active"
|
377
|
+
should_have_json "error", "invalid_token"
|
280
378
|
end
|
281
379
|
end
|
282
380
|
|
@@ -285,7 +383,7 @@ describe Doorkeeper::TokensController do
|
|
285
383
|
FactoryBot.create(:access_token, application: client, revoked_at: 1.day.ago)
|
286
384
|
end
|
287
385
|
|
288
|
-
it "responds with
|
386
|
+
it "responds with invalid_token error" do
|
289
387
|
request.headers["Authorization"] = "Bearer #{access_token.token}"
|
290
388
|
|
291
389
|
post :introspect, params: { token: token_for_introspection.token }
|
@@ -340,13 +438,15 @@ describe Doorkeeper::TokensController do
|
|
340
438
|
end
|
341
439
|
|
342
440
|
context "authorized using valid Bearer token" do
|
343
|
-
it "responds with
|
441
|
+
it "responds with invalid_token error" do
|
344
442
|
request.headers["Authorization"] = "Bearer #{access_token.token}"
|
345
443
|
|
346
444
|
post :introspect, params: { token: SecureRandom.hex(16) }
|
347
445
|
|
348
|
-
|
349
|
-
|
446
|
+
response_status_should_be 401
|
447
|
+
|
448
|
+
should_not_have_json "active"
|
449
|
+
should_have_json "error", "invalid_token"
|
350
450
|
end
|
351
451
|
end
|
352
452
|
end
|
@@ -107,32 +107,59 @@ Doorkeeper.configure do
|
|
107
107
|
# client.superapp? or resource_owner.admin?
|
108
108
|
# end
|
109
109
|
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
#
|
118
|
-
#
|
119
|
-
#
|
120
|
-
#
|
121
|
-
#
|
122
|
-
#
|
110
|
+
# Configure custom constraints for the Token Introspection request.
|
111
|
+
# By default this configuration option allows to introspect a token by another
|
112
|
+
# token of the same application, OR to introspect the token that belongs to
|
113
|
+
# authorized client (from authenticated client) OR when token doesn't
|
114
|
+
# belong to any client (public token). Otherwise requester has no access to the
|
115
|
+
# introspection and it will return response as stated in the RFC.
|
116
|
+
#
|
117
|
+
# Block arguments:
|
118
|
+
#
|
119
|
+
# @param token [Doorkeeper::AccessToken]
|
120
|
+
# token to be introspected
|
121
|
+
#
|
122
|
+
# @param authorized_client [Doorkeeper::Application]
|
123
|
+
# authorized client (if request is authorized using Basic auth with
|
124
|
+
# Client Credentials for example)
|
125
|
+
#
|
126
|
+
# @param authorized_token [Doorkeeper::AccessToken]
|
127
|
+
# Bearer token used to authorize the request
|
123
128
|
#
|
124
|
-
#
|
125
|
-
#
|
126
|
-
#
|
127
|
-
#
|
129
|
+
# In case the block returns `nil` or `false` introspection responses with 401 status code
|
130
|
+
# when using authorized token to introspect, or you'll get 200 with { "active": false } body
|
131
|
+
# when using authorized client to introspect as stated in the
|
132
|
+
# RFC 7662 section 2.2. Introspection Response.
|
128
133
|
#
|
129
|
-
#
|
130
|
-
#
|
131
|
-
#
|
134
|
+
# Using with caution:
|
135
|
+
# Keep in mind that these three parameters pass to block can be nil as following case:
|
136
|
+
# `authorized_client` is nil if and only if `authorized_token` is present, and vice versa.
|
137
|
+
# `token` will be nil if and only if `authorized_token` is present.
|
138
|
+
# So remember to use `&` or check if it is present before calling method on
|
139
|
+
# them to make sure you doesn't get NoMethodError exception.
|
140
|
+
#
|
141
|
+
# You can define your custom check:
|
142
|
+
#
|
143
|
+
# allow_token_introspection do |token, authorized_client, authorized_token|
|
144
|
+
# if authorized_token
|
145
|
+
# # customize: require `introspection` scope
|
146
|
+
# authorized_token.application == token&.application ||
|
147
|
+
# authorized_token.scopes.include?("introspection")
|
148
|
+
# elsif token.application
|
149
|
+
# # `protected_resource` is a new database boolean column, for example
|
150
|
+
# authorized_client == token.application || authorized_client.protected_resource?
|
132
151
|
# else
|
152
|
+
# # public token (when token.application is nil, token doesn't belong to any application)
|
133
153
|
# true
|
134
154
|
# end
|
135
155
|
# end
|
156
|
+
#
|
157
|
+
# Or you can completely disable any token introspection:
|
158
|
+
#
|
159
|
+
# allow_token_introspection false
|
160
|
+
#
|
161
|
+
# If you need to block the request at all, then configure your routes.rb or web-server
|
162
|
+
# like nginx to forbid the request.
|
136
163
|
|
137
164
|
# WWW-Authenticate Realm (default "Doorkeeper").
|
138
165
|
realm "Doorkeeper"
|
@@ -55,6 +55,7 @@ class Doorkeeper::OAuth::ClientCredentialsRequest
|
|
55
55
|
|
56
56
|
expect(Doorkeeper::AccessToken.count).to eq(2)
|
57
57
|
expect(result).not_to eq(existing_token)
|
58
|
+
expect(existing_token.reload).to be_revoked
|
58
59
|
end
|
59
60
|
end
|
60
61
|
|
@@ -69,6 +70,7 @@ class Doorkeeper::OAuth::ClientCredentialsRequest
|
|
69
70
|
|
70
71
|
expect(Doorkeeper::AccessToken.count).to eq(2)
|
71
72
|
expect(result).not_to eq(existing_token)
|
73
|
+
expect(existing_token.reload).to be_revoked
|
72
74
|
end
|
73
75
|
end
|
74
76
|
end
|
@@ -82,6 +84,7 @@ class Doorkeeper::OAuth::ClientCredentialsRequest
|
|
82
84
|
|
83
85
|
expect(Doorkeeper::AccessToken.count).to eq(2)
|
84
86
|
expect(result).not_to eq(existing_token)
|
87
|
+
expect(existing_token.reload).to be_revoked
|
85
88
|
end
|
86
89
|
end
|
87
90
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: doorkeeper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.2.0.
|
4
|
+
version: 5.2.0.rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Felipe Elias Philipp
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2019-
|
14
|
+
date: 2019-06-17 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: railties
|
@@ -179,6 +179,7 @@ files:
|
|
179
179
|
- CODE_OF_CONDUCT.md
|
180
180
|
- CONTRIBUTING.md
|
181
181
|
- Dangerfile
|
182
|
+
- Dockerfile
|
182
183
|
- Gemfile
|
183
184
|
- MIT-LICENSE
|
184
185
|
- NEWS.md
|