doorkeeper 4.2.5 → 4.3.0
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/.github/ISSUE_TEMPLATE.md +19 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +17 -0
- data/.gitignore +1 -1
- data/.hound.yml +2 -13
- data/.rubocop.yml +13 -0
- data/.travis.yml +13 -4
- data/Appraisals +6 -2
- data/CODE_OF_CONDUCT.md +46 -0
- data/Gemfile +1 -1
- data/NEWS.md +28 -0
- data/README.md +40 -10
- data/RELEASING.md +5 -12
- data/SECURITY.md +13 -0
- data/app/controllers/doorkeeper/application_controller.rb +1 -5
- data/app/controllers/doorkeeper/applications_controller.rb +14 -1
- data/app/controllers/doorkeeper/tokens_controller.rb +13 -1
- data/app/helpers/doorkeeper/dashboard_helper.rb +4 -2
- data/app/validators/redirect_uri_validator.rb +12 -2
- data/app/views/doorkeeper/applications/_form.html.erb +2 -2
- data/app/views/doorkeeper/authorizations/new.html.erb +1 -1
- data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
- data/config/locales/en.yml +3 -5
- data/doorkeeper.gemspec +4 -4
- data/gemfiles/rails_4_2.gemfile +6 -4
- data/gemfiles/rails_5_0.gemfile +4 -4
- data/gemfiles/rails_5_1.gemfile +6 -7
- data/gemfiles/rails_5_2.gemfile +12 -0
- data/gemfiles/rails_master.gemfile +14 -0
- data/lib/doorkeeper/config.rb +55 -55
- data/lib/doorkeeper/engine.rb +3 -3
- data/lib/doorkeeper/errors.rb +18 -0
- data/lib/doorkeeper/grape/helpers.rb +13 -8
- data/lib/doorkeeper/helpers/controller.rb +9 -20
- data/lib/doorkeeper/models/access_token_mixin.rb +14 -7
- data/lib/doorkeeper/models/application_mixin.rb +11 -6
- data/lib/doorkeeper/models/concerns/expirable.rb +7 -5
- data/lib/doorkeeper/oauth/authorization/token.rb +22 -18
- data/lib/doorkeeper/oauth/authorization_code_request.rb +6 -1
- data/lib/doorkeeper/oauth/base_request.rb +5 -5
- data/lib/doorkeeper/oauth/client/credentials.rb +2 -2
- data/lib/doorkeeper/oauth/client.rb +2 -2
- data/lib/doorkeeper/oauth/error.rb +2 -2
- data/lib/doorkeeper/oauth/error_response.rb +1 -2
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
- data/lib/doorkeeper/oauth/invalid_token_response.rb +2 -3
- data/lib/doorkeeper/oauth/password_access_token_request.rb +1 -0
- data/lib/doorkeeper/oauth/refresh_token_request.rb +1 -0
- data/lib/doorkeeper/oauth/scopes.rb +18 -8
- data/lib/doorkeeper/oauth/token.rb +1 -1
- data/lib/doorkeeper/oauth/token_introspection.rb +128 -0
- data/lib/doorkeeper/orm/active_record/access_grant.rb +1 -1
- data/lib/doorkeeper/orm/active_record/access_token.rb +1 -23
- data/lib/doorkeeper/orm/active_record/application.rb +1 -1
- data/lib/doorkeeper/orm/active_record/base_record.rb +11 -0
- data/lib/doorkeeper/orm/active_record.rb +20 -8
- data/lib/doorkeeper/rails/helpers.rb +5 -6
- data/lib/doorkeeper/rails/routes.rb +9 -7
- data/lib/doorkeeper/request.rb +7 -1
- data/lib/doorkeeper/validations.rb +3 -2
- data/lib/doorkeeper/version.rb +13 -1
- data/lib/doorkeeper.rb +1 -0
- data/lib/generators/doorkeeper/application_owner_generator.rb +11 -2
- data/lib/generators/doorkeeper/migration_generator.rb +13 -1
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +7 -1
- data/lib/generators/doorkeeper/templates/{add_owner_to_application_migration.rb → add_owner_to_application_migration.rb.erb} +1 -1
- data/lib/generators/doorkeeper/templates/{add_previous_refresh_token_to_access_tokens.rb → add_previous_refresh_token_to_access_tokens.rb.erb} +1 -1
- data/lib/generators/doorkeeper/templates/initializer.rb +19 -3
- data/lib/generators/doorkeeper/templates/{migration.rb → migration.rb.erb} +1 -1
- data/spec/controllers/applications_controller_spec.rb +15 -4
- data/spec/controllers/authorizations_controller_spec.rb +31 -16
- data/spec/controllers/protected_resources_controller_spec.rb +28 -19
- data/spec/controllers/token_info_controller_spec.rb +17 -13
- data/spec/controllers/tokens_controller_spec.rb +138 -4
- data/spec/dummy/config/initializers/doorkeeper.rb +1 -1
- data/spec/dummy/config/initializers/{active_record_belongs_to_required_by_default.rb → new_framework_defaults.rb} +1 -1
- data/spec/dummy/config/initializers/secret_token.rb +0 -1
- data/spec/factories.rb +1 -1
- data/spec/generators/application_owner_generator_spec.rb +24 -5
- data/spec/generators/migration_generator_spec.rb +24 -3
- data/spec/generators/previous_refresh_token_generator_spec.rb +57 -0
- data/spec/grape/grape_integration_spec.rb +135 -0
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
- data/spec/lib/config_spec.rb +115 -12
- data/spec/lib/models/expirable_spec.rb +0 -1
- data/spec/lib/models/revocable_spec.rb +2 -2
- data/spec/lib/oauth/authorization_code_request_spec.rb +39 -11
- data/spec/lib/oauth/base_request_spec.rb +2 -7
- data/spec/lib/oauth/client_credentials/creator_spec.rb +1 -1
- data/spec/lib/oauth/client_credentials_integration_spec.rb +1 -1
- data/spec/lib/oauth/client_credentials_request_spec.rb +1 -0
- data/spec/lib/oauth/code_request_spec.rb +1 -3
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +5 -0
- data/spec/lib/oauth/invalid_token_response_spec.rb +1 -1
- data/spec/lib/oauth/password_access_token_request_spec.rb +9 -3
- data/spec/lib/oauth/refresh_token_request_spec.rb +19 -7
- data/spec/lib/oauth/scopes_spec.rb +28 -1
- data/spec/lib/oauth/token_request_spec.rb +6 -8
- data/spec/lib/server_spec.rb +10 -0
- data/spec/models/doorkeeper/access_grant_spec.rb +1 -1
- data/spec/models/doorkeeper/access_token_spec.rb +72 -48
- data/spec/models/doorkeeper/application_spec.rb +51 -18
- data/spec/requests/applications/applications_request_spec.rb +5 -5
- data/spec/requests/endpoints/token_spec.rb +8 -1
- data/spec/requests/flows/authorization_code_errors_spec.rb +11 -1
- data/spec/requests/flows/authorization_code_spec.rb +1 -0
- data/spec/requests/flows/client_credentials_spec.rb +1 -1
- data/spec/requests/flows/implicit_grant_errors_spec.rb +2 -2
- data/spec/requests/flows/refresh_token_spec.rb +4 -4
- data/spec/requests/flows/revoke_token_spec.rb +15 -15
- data/spec/requests/protected_resources/metal_spec.rb +1 -1
- data/spec/requests/protected_resources/private_api_spec.rb +1 -1
- data/spec/routing/custom_controller_routes_spec.rb +4 -0
- data/spec/routing/default_routes_spec.rb +5 -1
- data/spec/spec_helper_integration.rb +15 -5
- data/spec/support/dependencies/factory_girl.rb +2 -2
- data/spec/support/helpers/access_token_request_helper.rb +1 -1
- data/spec/support/helpers/model_helper.rb +9 -4
- data/spec/support/helpers/request_spec_helper.rb +7 -3
- data/spec/support/helpers/url_helper.rb +8 -8
- data/spec/support/shared/controllers_shared_context.rb +2 -6
- data/spec/support/shared/models_shared_examples.rb +4 -4
- data/spec/validators/redirect_uri_validator_spec.rb +51 -6
- data/spec/version/version_spec.rb +15 -0
- metadata +42 -27
|
@@ -60,13 +60,15 @@ Doorkeeper.configure do
|
|
|
60
60
|
# Change the way client credentials are retrieved from the request object.
|
|
61
61
|
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
|
|
62
62
|
# falls back to the `:client_id` and `:client_secret` params from the `params` object.
|
|
63
|
-
# Check out
|
|
63
|
+
# Check out https://github.com/doorkeeper-gem/doorkeeper/wiki/Changing-how-clients-are-authenticated
|
|
64
|
+
# for more information on customization
|
|
64
65
|
# client_credentials :from_basic, :from_params
|
|
65
66
|
|
|
66
67
|
# Change the way access token is authenticated from the request object.
|
|
67
68
|
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
|
|
68
69
|
# falls back to the `:access_token` or `:bearer_token` params from the `params` object.
|
|
69
|
-
# Check out
|
|
70
|
+
# Check out https://github.com/doorkeeper-gem/doorkeeper/wiki/Changing-how-clients-are-authenticated
|
|
71
|
+
# for more information on customization
|
|
70
72
|
# access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param
|
|
71
73
|
|
|
72
74
|
# Change the native redirect uri for client apps
|
|
@@ -80,7 +82,21 @@ Doorkeeper.configure do
|
|
|
80
82
|
# by default in non-development environments). OAuth2 delegates security in
|
|
81
83
|
# communication to the HTTPS protocol so it is wise to keep this enabled.
|
|
82
84
|
#
|
|
85
|
+
# Callable objects such as proc, lambda, block or any object that responds to
|
|
86
|
+
# #call can be used in order to allow conditional checks (to allow non-SSL
|
|
87
|
+
# redirects to localhost for example).
|
|
88
|
+
#
|
|
83
89
|
# force_ssl_in_redirect_uri !Rails.env.development?
|
|
90
|
+
#
|
|
91
|
+
# force_ssl_in_redirect_uri { |uri| uri.host != 'localhost' }
|
|
92
|
+
|
|
93
|
+
# Specify what redirect URI's you want to block during creation. Any redirect
|
|
94
|
+
# URI is whitelisted by default.
|
|
95
|
+
#
|
|
96
|
+
# You can use this option in order to forbid URI's with 'javascript' scheme
|
|
97
|
+
# for example.
|
|
98
|
+
#
|
|
99
|
+
# forbid_redirect_uri { |uri| uri.scheme.to_s.downcase == 'javascript' }
|
|
84
100
|
|
|
85
101
|
# Specify what grant flows are enabled in array of Strings. The valid
|
|
86
102
|
# strings and the flows they enable are:
|
|
@@ -98,7 +114,7 @@ Doorkeeper.configure do
|
|
|
98
114
|
# http://tools.ietf.org/html/rfc6819#section-4.4.2
|
|
99
115
|
# http://tools.ietf.org/html/rfc6819#section-4.4.3
|
|
100
116
|
#
|
|
101
|
-
# grant_flows %w
|
|
117
|
+
# grant_flows %w[authorization_code client_credentials]
|
|
102
118
|
|
|
103
119
|
# Under some circumstances you might want to have applications auto-approved,
|
|
104
120
|
# so that the user skips the authorization step.
|
|
@@ -19,13 +19,24 @@ module Doorkeeper
|
|
|
19
19
|
post :create, doorkeeper_application: {
|
|
20
20
|
name: 'Example',
|
|
21
21
|
redirect_uri: 'https://example.com' }
|
|
22
|
-
end.
|
|
22
|
+
end.not_to change { Doorkeeper::Application.count }
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
context 'when admin is authenticated' do
|
|
27
|
+
render_views
|
|
28
|
+
|
|
27
29
|
before do
|
|
28
|
-
allow(Doorkeeper.configuration).to receive(:authenticate_admin).and_return(->(
|
|
30
|
+
allow(Doorkeeper.configuration).to receive(:authenticate_admin).and_return(->(*) { true })
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'sorts applications by created_at' do
|
|
34
|
+
first_application = FactoryBot.create(:application)
|
|
35
|
+
second_application = FactoryBot.create(:application)
|
|
36
|
+
expect(Doorkeeper::Application).to receive(:ordered_by).and_call_original
|
|
37
|
+
get :index
|
|
38
|
+
expect(response.body).to have_selector("tbody tr:first-child#application_#{first_application.id}")
|
|
39
|
+
expect(response.body).to have_selector("tbody tr:last-child#application_#{second_application.id}")
|
|
29
40
|
end
|
|
30
41
|
|
|
31
42
|
it 'creates application' do
|
|
@@ -38,7 +49,7 @@ module Doorkeeper
|
|
|
38
49
|
end
|
|
39
50
|
|
|
40
51
|
it 'does not allow mass assignment of uid or secret' do
|
|
41
|
-
application =
|
|
52
|
+
application = FactoryBot.create(:application)
|
|
42
53
|
put :update, id: application.id, doorkeeper_application: {
|
|
43
54
|
uid: '1A2B3C4D',
|
|
44
55
|
secret: '1A2B3C4D' }
|
|
@@ -47,7 +58,7 @@ module Doorkeeper
|
|
|
47
58
|
end
|
|
48
59
|
|
|
49
60
|
it 'updates application' do
|
|
50
|
-
application =
|
|
61
|
+
application = FactoryBot.create(:application)
|
|
51
62
|
put :update, id: application.id, doorkeeper_application: {
|
|
52
63
|
name: 'Example',
|
|
53
64
|
redirect_uri: 'https://example.com' }
|
|
@@ -3,18 +3,33 @@ require 'spec_helper_integration'
|
|
|
3
3
|
describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
|
|
4
4
|
include AuthorizationRequestHelper
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
if Rails::VERSION::MAJOR >= 5
|
|
7
|
+
class ActionDispatch::TestResponse
|
|
8
|
+
def query_params
|
|
9
|
+
@_query_params ||= begin
|
|
10
|
+
fragment = URI.parse(location).fragment
|
|
11
|
+
Rack::Utils.parse_query(fragment)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
else
|
|
16
|
+
class ActionController::TestResponse
|
|
17
|
+
def query_params
|
|
18
|
+
@_query_params ||= begin
|
|
19
|
+
fragment = URI.parse(location).fragment
|
|
20
|
+
Rack::Utils.parse_query(fragment)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
9
24
|
end
|
|
10
25
|
|
|
11
26
|
def translated_error_message(key)
|
|
12
27
|
I18n.translate key, scope: [:doorkeeper, :errors, :messages]
|
|
13
28
|
end
|
|
14
29
|
|
|
15
|
-
let(:client) {
|
|
30
|
+
let(:client) { FactoryBot.create :application }
|
|
16
31
|
let(:user) { User.create!(name: 'Joe', password: 'sekret') }
|
|
17
|
-
let(:access_token) {
|
|
32
|
+
let(:access_token) { FactoryBot.build :access_token, resource_owner_id: user.id, application_id: client.id }
|
|
18
33
|
|
|
19
34
|
before do
|
|
20
35
|
allow(Doorkeeper.configuration).to receive(:grant_flows).and_return(["implicit"])
|
|
@@ -35,15 +50,15 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
|
|
|
35
50
|
end
|
|
36
51
|
|
|
37
52
|
it 'includes access token in fragment' do
|
|
38
|
-
expect(
|
|
53
|
+
expect(response.query_params['access_token']).to eq(Doorkeeper::AccessToken.first.token)
|
|
39
54
|
end
|
|
40
55
|
|
|
41
56
|
it 'includes token type in fragment' do
|
|
42
|
-
expect(
|
|
57
|
+
expect(response.query_params['token_type']).to eq('bearer')
|
|
43
58
|
end
|
|
44
59
|
|
|
45
60
|
it 'includes token expiration in fragment' do
|
|
46
|
-
expect(
|
|
61
|
+
expect(response.query_params['expires_in'].to_i).to eq(2.hours.to_i)
|
|
47
62
|
end
|
|
48
63
|
|
|
49
64
|
it 'issues the token for the current client' do
|
|
@@ -70,15 +85,15 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
|
|
|
70
85
|
end
|
|
71
86
|
|
|
72
87
|
it 'does not include access token in fragment' do
|
|
73
|
-
expect(
|
|
88
|
+
expect(response.query_params['access_token']).to be_nil
|
|
74
89
|
end
|
|
75
90
|
|
|
76
91
|
it 'includes error in fragment' do
|
|
77
|
-
expect(
|
|
92
|
+
expect(response.query_params['error']).to eq('invalid_scope')
|
|
78
93
|
end
|
|
79
94
|
|
|
80
95
|
it 'includes error description in fragment' do
|
|
81
|
-
expect(
|
|
96
|
+
expect(response.query_params['error_description']).to eq(translated_error_message(:invalid_scope))
|
|
82
97
|
end
|
|
83
98
|
|
|
84
99
|
it 'does not issue any access token' do
|
|
@@ -95,7 +110,7 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
|
|
|
95
110
|
end
|
|
96
111
|
|
|
97
112
|
it 'returns the existing access token in a fragment' do
|
|
98
|
-
expect(
|
|
113
|
+
expect(response.query_params['access_token']).to eq(access_token.token)
|
|
99
114
|
end
|
|
100
115
|
|
|
101
116
|
it 'does not creates a new access token' do
|
|
@@ -129,7 +144,7 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
|
|
|
129
144
|
describe 'GET #new code request with native url and skip_authorization true' do
|
|
130
145
|
before do
|
|
131
146
|
allow(Doorkeeper.configuration).to receive(:grant_flows).
|
|
132
|
-
and_return(%w
|
|
147
|
+
and_return(%w[authorization_code])
|
|
133
148
|
allow(Doorkeeper.configuration).to receive(:skip_authorization).and_return(proc do
|
|
134
149
|
true
|
|
135
150
|
end)
|
|
@@ -139,7 +154,7 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
|
|
|
139
154
|
|
|
140
155
|
it 'should redirect immediately' do
|
|
141
156
|
expect(response).to be_redirect
|
|
142
|
-
expect(response.location).to match(/oauth\/authorize
|
|
157
|
+
expect(response.location).to match(/oauth\/authorize\/native\?code=#{Doorkeeper::AccessGrant.first.token}/)
|
|
143
158
|
end
|
|
144
159
|
|
|
145
160
|
it 'should issue a grant' do
|
|
@@ -169,11 +184,11 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
|
|
|
169
184
|
end
|
|
170
185
|
|
|
171
186
|
it 'includes token type in fragment' do
|
|
172
|
-
expect(
|
|
187
|
+
expect(response.query_params['token_type']).to eq('bearer')
|
|
173
188
|
end
|
|
174
189
|
|
|
175
190
|
it 'includes token expiration in fragment' do
|
|
176
|
-
expect(
|
|
191
|
+
expect(response.query_params['expires_in'].to_i).to eq(2.hours.to_i)
|
|
177
192
|
end
|
|
178
193
|
|
|
179
194
|
it 'issues the token for the current client' do
|
|
@@ -9,11 +9,9 @@ module ControllerActions
|
|
|
9
9
|
render plain: 'show'
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def doorkeeper_unauthorized_render_options(*)
|
|
13
|
-
end
|
|
12
|
+
def doorkeeper_unauthorized_render_options(*); end
|
|
14
13
|
|
|
15
|
-
def doorkeeper_forbidden_render_options(*)
|
|
16
|
-
end
|
|
14
|
+
def doorkeeper_forbidden_render_options(*); end
|
|
17
15
|
end
|
|
18
16
|
|
|
19
17
|
describe 'doorkeeper authorize filter' do
|
|
@@ -74,12 +72,12 @@ describe 'doorkeeper authorize filter' do
|
|
|
74
72
|
context 'with valid token', token: :valid do
|
|
75
73
|
it 'allows into index action' do
|
|
76
74
|
get :index, access_token: token_string
|
|
77
|
-
expect(response).to
|
|
75
|
+
expect(response).to be_successful
|
|
78
76
|
end
|
|
79
77
|
|
|
80
78
|
it 'allows into show action' do
|
|
81
79
|
get :show, id: '4', access_token: token_string
|
|
82
|
-
expect(response).to
|
|
80
|
+
expect(response).to be_successful
|
|
83
81
|
end
|
|
84
82
|
end
|
|
85
83
|
|
|
@@ -109,15 +107,16 @@ describe 'doorkeeper authorize filter' do
|
|
|
109
107
|
|
|
110
108
|
it 'allows if the token has particular scopes' do
|
|
111
109
|
token = double(Doorkeeper::AccessToken,
|
|
112
|
-
accessible?: true, scopes: %w
|
|
110
|
+
accessible?: true, scopes: %w[write public],
|
|
113
111
|
previous_refresh_token: "",
|
|
114
112
|
revoke_previous_refresh_token!: true)
|
|
115
113
|
expect(token).to receive(:acceptable?).with([:write]).and_return(true)
|
|
116
114
|
expect(
|
|
117
115
|
Doorkeeper::AccessToken
|
|
118
116
|
).to receive(:by_token).with(token_string).and_return(token)
|
|
117
|
+
|
|
119
118
|
get :index, access_token: token_string
|
|
120
|
-
expect(response).to
|
|
119
|
+
expect(response).to be_successful
|
|
121
120
|
end
|
|
122
121
|
|
|
123
122
|
it 'does not allow if the token does not include given scope' do
|
|
@@ -129,6 +128,7 @@ describe 'doorkeeper authorize filter' do
|
|
|
129
128
|
Doorkeeper::AccessToken
|
|
130
129
|
).to receive(:by_token).with(token_string).and_return(token)
|
|
131
130
|
expect(token).to receive(:acceptable?).with([:write]).and_return(false)
|
|
131
|
+
|
|
132
132
|
get :index, access_token: token_string
|
|
133
133
|
expect(response.status).to eq 403
|
|
134
134
|
expect(response.header).to_not include('WWW-Authenticate')
|
|
@@ -146,14 +146,17 @@ describe 'doorkeeper authorize filter' do
|
|
|
146
146
|
before do
|
|
147
147
|
module ControllerActions
|
|
148
148
|
remove_method :doorkeeper_unauthorized_render_options
|
|
149
|
+
|
|
149
150
|
def doorkeeper_unauthorized_render_options(error: nil)
|
|
150
151
|
{ json: ActiveSupport::JSON.encode(error_message: error.description) }
|
|
151
152
|
end
|
|
152
153
|
end
|
|
153
154
|
end
|
|
155
|
+
|
|
154
156
|
after do
|
|
155
157
|
module ControllerActions
|
|
156
158
|
remove_method :doorkeeper_unauthorized_render_options
|
|
159
|
+
|
|
157
160
|
def doorkeeper_unauthorized_render_options(error: nil)
|
|
158
161
|
end
|
|
159
162
|
end
|
|
@@ -164,9 +167,9 @@ describe 'doorkeeper authorize filter' do
|
|
|
164
167
|
expect(response.status).to eq 401
|
|
165
168
|
expect(response.content_type).to eq('application/json')
|
|
166
169
|
expect(response.header['WWW-Authenticate']).to match(/^Bearer/)
|
|
167
|
-
|
|
168
|
-
expect(
|
|
169
|
-
expect(
|
|
170
|
+
|
|
171
|
+
expect(json_response).not_to be_nil
|
|
172
|
+
expect(json_response['error_message']).to match('token is invalid')
|
|
170
173
|
end
|
|
171
174
|
end
|
|
172
175
|
|
|
@@ -174,16 +177,18 @@ describe 'doorkeeper authorize filter' do
|
|
|
174
177
|
before do
|
|
175
178
|
module ControllerActions
|
|
176
179
|
remove_method :doorkeeper_unauthorized_render_options
|
|
177
|
-
|
|
180
|
+
|
|
181
|
+
def doorkeeper_unauthorized_render_options(**)
|
|
178
182
|
{ plain: 'Unauthorized' }
|
|
179
183
|
end
|
|
180
184
|
end
|
|
181
185
|
end
|
|
186
|
+
|
|
182
187
|
after do
|
|
183
188
|
module ControllerActions
|
|
184
189
|
remove_method :doorkeeper_unauthorized_render_options
|
|
185
|
-
|
|
186
|
-
end
|
|
190
|
+
|
|
191
|
+
def doorkeeper_unauthorized_render_options(error: nil); end
|
|
187
192
|
end
|
|
188
193
|
end
|
|
189
194
|
|
|
@@ -206,8 +211,8 @@ describe 'doorkeeper authorize filter' do
|
|
|
206
211
|
after do
|
|
207
212
|
module ControllerActions
|
|
208
213
|
remove_method :doorkeeper_forbidden_render_options
|
|
209
|
-
|
|
210
|
-
end
|
|
214
|
+
|
|
215
|
+
def doorkeeper_forbidden_render_options(*); end
|
|
211
216
|
end
|
|
212
217
|
end
|
|
213
218
|
|
|
@@ -223,12 +228,14 @@ describe 'doorkeeper authorize filter' do
|
|
|
223
228
|
expired?: false, previous_refresh_token: "",
|
|
224
229
|
revoke_previous_refresh_token!: true)
|
|
225
230
|
end
|
|
231
|
+
|
|
226
232
|
let(:token_string) { '1A2DUWE' }
|
|
227
233
|
|
|
228
234
|
context 'with a JSON custom render' do
|
|
229
235
|
before do
|
|
230
236
|
module ControllerActions
|
|
231
237
|
remove_method :doorkeeper_forbidden_render_options
|
|
238
|
+
|
|
232
239
|
def doorkeeper_forbidden_render_options(*)
|
|
233
240
|
{ json: { error_message: 'Forbidden' } }
|
|
234
241
|
end
|
|
@@ -240,9 +247,9 @@ describe 'doorkeeper authorize filter' do
|
|
|
240
247
|
expect(response.header).to_not include('WWW-Authenticate')
|
|
241
248
|
expect(response.content_type).to eq('application/json')
|
|
242
249
|
expect(response.status).to eq 403
|
|
243
|
-
|
|
244
|
-
expect(
|
|
245
|
-
expect(
|
|
250
|
+
|
|
251
|
+
expect(json_response).not_to be_nil
|
|
252
|
+
expect(json_response['error_message']).to match('Forbidden')
|
|
246
253
|
end
|
|
247
254
|
end
|
|
248
255
|
|
|
@@ -267,6 +274,7 @@ describe 'doorkeeper authorize filter' do
|
|
|
267
274
|
before do
|
|
268
275
|
module ControllerActions
|
|
269
276
|
remove_method :doorkeeper_forbidden_render_options
|
|
277
|
+
|
|
270
278
|
def doorkeeper_forbidden_render_options(*)
|
|
271
279
|
{ plain: 'Forbidden' }
|
|
272
280
|
end
|
|
@@ -285,6 +293,7 @@ describe 'doorkeeper authorize filter' do
|
|
|
285
293
|
before do
|
|
286
294
|
module ControllerActions
|
|
287
295
|
remove_method :doorkeeper_forbidden_render_options
|
|
296
|
+
|
|
288
297
|
def doorkeeper_forbidden_render_options(*)
|
|
289
298
|
{ respond_not_found_when_forbidden: true, plain: 'Not Found' }
|
|
290
299
|
end
|
|
@@ -1,26 +1,23 @@
|
|
|
1
1
|
require 'spec_helper_integration'
|
|
2
2
|
|
|
3
3
|
describe Doorkeeper::TokenInfoController do
|
|
4
|
-
describe 'when requesting
|
|
5
|
-
let(:doorkeeper_token) {
|
|
4
|
+
describe 'when requesting token info with valid token' do
|
|
5
|
+
let(:doorkeeper_token) { FactoryBot.create(:access_token) }
|
|
6
6
|
|
|
7
7
|
before(:each) do
|
|
8
8
|
allow(controller).to receive(:doorkeeper_token) { doorkeeper_token }
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def do_get
|
|
12
|
-
get :show
|
|
13
|
-
end
|
|
14
|
-
|
|
15
11
|
describe 'successful request' do
|
|
16
|
-
|
|
17
12
|
it 'responds with tokeninfo' do
|
|
18
|
-
|
|
13
|
+
get :show
|
|
14
|
+
|
|
19
15
|
expect(response.body).to eq(doorkeeper_token.to_json)
|
|
20
16
|
end
|
|
21
17
|
|
|
22
18
|
it 'responds with a 200 status' do
|
|
23
|
-
|
|
19
|
+
get :show
|
|
20
|
+
|
|
24
21
|
expect(response.status).to eq 200
|
|
25
22
|
end
|
|
26
23
|
end
|
|
@@ -29,8 +26,10 @@ describe Doorkeeper::TokenInfoController do
|
|
|
29
26
|
before(:each) do
|
|
30
27
|
allow(controller).to receive(:doorkeeper_token).and_return(nil)
|
|
31
28
|
end
|
|
29
|
+
|
|
32
30
|
it 'responds with 401 when doorkeeper_token is not valid' do
|
|
33
|
-
|
|
31
|
+
get :show
|
|
32
|
+
|
|
34
33
|
expect(response.status).to eq 401
|
|
35
34
|
expect(response.headers['WWW-Authenticate']).to match(/^Bearer/)
|
|
36
35
|
end
|
|
@@ -38,14 +37,19 @@ describe Doorkeeper::TokenInfoController do
|
|
|
38
37
|
it 'responds with 401 when doorkeeper_token is invalid, expired or revoked' do
|
|
39
38
|
allow(controller).to receive(:doorkeeper_token).and_return(doorkeeper_token)
|
|
40
39
|
allow(doorkeeper_token).to receive(:accessible?).and_return(false)
|
|
41
|
-
|
|
40
|
+
|
|
41
|
+
get :show
|
|
42
|
+
|
|
42
43
|
expect(response.status).to eq 401
|
|
43
44
|
expect(response.headers['WWW-Authenticate']).to match(/^Bearer/)
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
it 'responds body message for error' do
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
get :show
|
|
49
|
+
|
|
50
|
+
expect(response.body).to eq(
|
|
51
|
+
Doorkeeper::OAuth::ErrorResponse.new(name: :invalid_request, status: :unauthorized).body.to_json
|
|
52
|
+
)
|
|
49
53
|
end
|
|
50
54
|
end
|
|
51
55
|
end
|
|
@@ -2,9 +2,7 @@ require 'spec_helper_integration'
|
|
|
2
2
|
|
|
3
3
|
describe Doorkeeper::TokensController do
|
|
4
4
|
describe 'when authorization has succeeded' do
|
|
5
|
-
let :token
|
|
6
|
-
double(:token, authorize: true)
|
|
7
|
-
end
|
|
5
|
+
let(:token) { double(:token, authorize: true) }
|
|
8
6
|
|
|
9
7
|
before do
|
|
10
8
|
allow(controller).to receive(:token) { token }
|
|
@@ -57,7 +55,7 @@ describe Doorkeeper::TokensController do
|
|
|
57
55
|
}
|
|
58
56
|
expect(response.status).to eq 401
|
|
59
57
|
expect(response.headers['WWW-Authenticate']).to match(/Bearer/)
|
|
60
|
-
expect(JSON.
|
|
58
|
+
expect(JSON.parse(response.body)).to eq expected_response_body
|
|
61
59
|
end
|
|
62
60
|
end
|
|
63
61
|
|
|
@@ -85,4 +83,140 @@ describe Doorkeeper::TokensController do
|
|
|
85
83
|
post :create
|
|
86
84
|
end
|
|
87
85
|
end
|
|
86
|
+
|
|
87
|
+
describe 'when requested token introspection' do
|
|
88
|
+
context 'authorized using Bearer token' do
|
|
89
|
+
let(:client) { FactoryBot.create(:application) }
|
|
90
|
+
let(:access_token) { FactoryBot.create(:access_token, application: client) }
|
|
91
|
+
|
|
92
|
+
it 'responds with full token introspection' do
|
|
93
|
+
request.headers['Authorization'] = "Bearer #{access_token.token}"
|
|
94
|
+
|
|
95
|
+
post :introspect, token: access_token.token
|
|
96
|
+
|
|
97
|
+
should_have_json 'active', true
|
|
98
|
+
expect(json_response).to include('client_id', 'token_type', 'exp', 'iat')
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
context 'authorized using Client Authentication' do
|
|
103
|
+
let(:client) { FactoryBot.create(:application) }
|
|
104
|
+
let(:access_token) { FactoryBot.create(:access_token, application: client) }
|
|
105
|
+
|
|
106
|
+
it 'responds with full token introspection' do
|
|
107
|
+
request.headers['Authorization'] = basic_auth_header_for_client(client)
|
|
108
|
+
|
|
109
|
+
post :introspect, token: access_token.token
|
|
110
|
+
|
|
111
|
+
should_have_json 'active', true
|
|
112
|
+
expect(json_response).to include('client_id', 'token_type', 'exp', 'iat')
|
|
113
|
+
should_have_json 'client_id', client.uid
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
context 'public access token' do
|
|
118
|
+
let(:client) { FactoryBot.create(:application) }
|
|
119
|
+
let(:access_token) { FactoryBot.create(:access_token, application: nil) }
|
|
120
|
+
|
|
121
|
+
it 'responds with full token introspection' do
|
|
122
|
+
request.headers['Authorization'] = basic_auth_header_for_client(client)
|
|
123
|
+
|
|
124
|
+
post :introspect, token: access_token.token
|
|
125
|
+
|
|
126
|
+
should_have_json 'active', true
|
|
127
|
+
expect(json_response).to include('client_id', 'token_type', 'exp', 'iat')
|
|
128
|
+
should_have_json 'client_id', nil
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
context 'token was issued to a different client than is making this request' do
|
|
133
|
+
let(:client) { FactoryBot.create(:application) }
|
|
134
|
+
let(:different_client) { FactoryBot.create(:application) }
|
|
135
|
+
let(:access_token) { FactoryBot.create(:access_token, application: client) }
|
|
136
|
+
|
|
137
|
+
it 'responds with only active state' do
|
|
138
|
+
request.headers['Authorization'] = basic_auth_header_for_client(different_client)
|
|
139
|
+
|
|
140
|
+
post :introspect, token: access_token.token
|
|
141
|
+
|
|
142
|
+
expect(response).to be_successful
|
|
143
|
+
|
|
144
|
+
should_have_json 'active', false
|
|
145
|
+
expect(json_response).not_to include('client_id', 'token_type', 'exp', 'iat')
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
context 'using invalid credentials to authorize' do
|
|
150
|
+
let(:client) { double(uid: '123123', secret: '666999') }
|
|
151
|
+
let(:access_token) { FactoryBot.create(:access_token) }
|
|
152
|
+
|
|
153
|
+
it 'responds with invalid_client error' do
|
|
154
|
+
request.headers['Authorization'] = basic_auth_header_for_client(client)
|
|
155
|
+
|
|
156
|
+
post :introspect, token: access_token.token
|
|
157
|
+
|
|
158
|
+
expect(response).not_to be_successful
|
|
159
|
+
response_status_should_be 401
|
|
160
|
+
|
|
161
|
+
should_not_have_json 'active'
|
|
162
|
+
should_have_json 'error', 'invalid_client'
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
context 'using wrong token value' do
|
|
167
|
+
let(:client) { FactoryBot.create(:application) }
|
|
168
|
+
let(:access_token) { FactoryBot.create(:access_token, application: client) }
|
|
169
|
+
|
|
170
|
+
it 'responds with only active state' do
|
|
171
|
+
request.headers['Authorization'] = basic_auth_header_for_client(client)
|
|
172
|
+
|
|
173
|
+
post :introspect, token: SecureRandom.hex(16)
|
|
174
|
+
|
|
175
|
+
should_have_json 'active', false
|
|
176
|
+
expect(json_response).not_to include('client_id', 'token_type', 'exp', 'iat')
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
context 'when requested Access Token expired' do
|
|
181
|
+
let(:client) { FactoryBot.create(:application) }
|
|
182
|
+
let(:access_token) { FactoryBot.create(:access_token, application: client, created_at: 1.year.ago) }
|
|
183
|
+
|
|
184
|
+
it 'responds with only active state' do
|
|
185
|
+
request.headers['Authorization'] = basic_auth_header_for_client(client)
|
|
186
|
+
|
|
187
|
+
post :introspect, token: access_token.token
|
|
188
|
+
|
|
189
|
+
should_have_json 'active', false
|
|
190
|
+
expect(json_response).not_to include('client_id', 'token_type', 'exp', 'iat')
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
context 'when requested Access Token revoked' do
|
|
195
|
+
let(:client) { FactoryBot.create(:application) }
|
|
196
|
+
let(:access_token) { FactoryBot.create(:access_token, application: client, revoked_at: 1.year.ago) }
|
|
197
|
+
|
|
198
|
+
it 'responds with only active state' do
|
|
199
|
+
request.headers['Authorization'] = basic_auth_header_for_client(client)
|
|
200
|
+
|
|
201
|
+
post :introspect, token: access_token.token
|
|
202
|
+
|
|
203
|
+
should_have_json 'active', false
|
|
204
|
+
expect(json_response).not_to include('client_id', 'token_type', 'exp', 'iat')
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
context 'unauthorized (no bearer token or client credentials)' do
|
|
209
|
+
let(:access_token) { FactoryBot.create(:access_token) }
|
|
210
|
+
|
|
211
|
+
it 'responds with invalid_request error' do
|
|
212
|
+
post :introspect, token: access_token.token
|
|
213
|
+
|
|
214
|
+
expect(response).not_to be_successful
|
|
215
|
+
response_status_should_be 401
|
|
216
|
+
|
|
217
|
+
should_not_have_json 'active'
|
|
218
|
+
should_have_json 'error', 'invalid_request'
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
88
222
|
end
|
|
@@ -82,7 +82,7 @@ Doorkeeper.configure do
|
|
|
82
82
|
# http://tools.ietf.org/html/rfc6819#section-4.4.2
|
|
83
83
|
# http://tools.ietf.org/html/rfc6819#section-4.4.3
|
|
84
84
|
#
|
|
85
|
-
# grant_flows %w
|
|
85
|
+
# grant_flows %w[authorization_code client_credentials]
|
|
86
86
|
|
|
87
87
|
# Under some circumstances you might want to have applications auto-approved,
|
|
88
88
|
# so that the user skips the authorization step.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Require `belongs_to` associations by default. This is a new Rails 5.0
|
|
2
2
|
# default, so it is introduced as a configuration option to ensure that apps
|
|
3
3
|
# made on earlier versions of Rails are not affected when upgrading.
|
|
4
|
-
if Rails
|
|
4
|
+
if Rails::VERSION::MAJOR >= 5
|
|
5
5
|
Rails.application.config.active_record.belongs_to_required_by_default = true
|
|
6
6
|
end
|
|
@@ -5,5 +5,4 @@
|
|
|
5
5
|
# Make sure the secret is at least 30 characters and all random,
|
|
6
6
|
# no regular words or you'll be exposed to dictionary attacks.
|
|
7
7
|
Dummy::Application.config.secret_key_base =
|
|
8
|
-
Dummy::Application.config.secret_token =
|
|
9
8
|
'c00157b5a1bb6181792f0f4a8a080485de7bab9987e6cf159dc74c4f0573345c1bfa713b5d756e1491fc0b098567e8a619e2f8d268eda86a20a720d05d633780'
|
data/spec/factories.rb
CHANGED
|
@@ -10,13 +10,32 @@ describe 'Doorkeeper::ApplicationOwnerGenerator' do
|
|
|
10
10
|
describe 'after running the generator' do
|
|
11
11
|
before :each do
|
|
12
12
|
prepare_destination
|
|
13
|
-
FileUtils.mkdir(::File.expand_path('config', Pathname(destination_root)))
|
|
14
|
-
FileUtils.copy_file(::File.expand_path('../templates/routes.rb', __FILE__), ::File.expand_path('config/routes.rb', Pathname.new(destination_root)))
|
|
15
|
-
run_generator
|
|
16
13
|
end
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
context 'pre Rails 5.0.0' do
|
|
16
|
+
it 'creates a migration with no version specifier' do
|
|
17
|
+
stub_const("ActiveRecord::VERSION::MAJOR", 4)
|
|
18
|
+
stub_const("ActiveRecord::VERSION::MINOR", 2)
|
|
19
|
+
|
|
20
|
+
run_generator
|
|
21
|
+
|
|
22
|
+
assert_migration 'db/migrate/add_owner_to_application.rb' do |migration|
|
|
23
|
+
assert migration.include?("ActiveRecord::Migration\n")
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
context 'post Rails 5.0.0' do
|
|
29
|
+
it 'creates a migration with a version specifier' do
|
|
30
|
+
stub_const("ActiveRecord::VERSION::MAJOR", 5)
|
|
31
|
+
stub_const("ActiveRecord::VERSION::MINOR", 0)
|
|
32
|
+
|
|
33
|
+
run_generator
|
|
34
|
+
|
|
35
|
+
assert_migration 'db/migrate/add_owner_to_application.rb' do |migration|
|
|
36
|
+
assert migration.include?("ActiveRecord::Migration[5.0]\n")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
20
39
|
end
|
|
21
40
|
end
|
|
22
41
|
end
|