doorkeeper 5.0.3 → 5.1.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/.hound.yml +2 -1
- data/.rubocop.yml +37 -4
- data/.travis.yml +11 -21
- data/Appraisals +29 -7
- data/Dangerfile +5 -2
- data/Gemfile +19 -5
- data/NEWS.md +81 -21
- data/README.md +75 -485
- data/Rakefile +10 -8
- data/app/controllers/doorkeeper/application_controller.rb +1 -2
- data/app/controllers/doorkeeper/application_metal_controller.rb +2 -13
- data/app/controllers/doorkeeper/applications_controller.rb +20 -8
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -1
- data/app/controllers/doorkeeper/token_info_controller.rb +2 -2
- data/app/controllers/doorkeeper/tokens_controller.rb +11 -11
- data/app/helpers/doorkeeper/dashboard_helper.rb +1 -1
- data/app/validators/redirect_uri_validator.rb +5 -2
- data/app/views/doorkeeper/applications/_form.html.erb +6 -0
- data/app/views/doorkeeper/applications/show.html.erb +1 -1
- data/app/views/layouts/doorkeeper/admin.html.erb +5 -3
- data/bin/console +16 -0
- data/config/locales/en.yml +1 -0
- data/doorkeeper.gemspec +24 -21
- data/gemfiles/rails_5_0.gemfile +9 -4
- data/gemfiles/rails_5_1.gemfile +9 -4
- data/gemfiles/rails_5_2.gemfile +10 -5
- data/gemfiles/rails_6_0.gemfile +17 -0
- data/gemfiles/rails_master.gemfile +9 -9
- data/lib/doorkeeper/config/option.rb +64 -0
- data/lib/doorkeeper/config.rb +191 -75
- data/lib/doorkeeper/engine.rb +1 -1
- data/lib/doorkeeper/grape/authorization_decorator.rb +4 -4
- data/lib/doorkeeper/grape/helpers.rb +3 -3
- data/lib/doorkeeper/helpers/controller.rb +3 -2
- data/lib/doorkeeper/models/access_grant_mixin.rb +20 -3
- data/lib/doorkeeper/models/access_token_mixin.rb +63 -19
- data/lib/doorkeeper/models/application_mixin.rb +43 -1
- data/lib/doorkeeper/models/concerns/expirable.rb +4 -2
- data/lib/doorkeeper/models/concerns/ownership.rb +1 -6
- data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
- data/lib/doorkeeper/models/concerns/revocable.rb +2 -1
- data/lib/doorkeeper/models/concerns/scopes.rb +5 -1
- data/lib/doorkeeper/models/concerns/secret_storable.rb +108 -0
- data/lib/doorkeeper/oauth/authorization/code.rb +2 -2
- data/lib/doorkeeper/oauth/authorization/token.rb +12 -7
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +1 -1
- data/lib/doorkeeper/oauth/authorization_code_request.rb +6 -4
- data/lib/doorkeeper/oauth/client.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +5 -4
- data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -1
- data/lib/doorkeeper/oauth/code_response.rb +2 -2
- data/lib/doorkeeper/oauth/error_response.rb +10 -6
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +23 -8
- data/lib/doorkeeper/oauth/helpers/unique_token.rb +13 -1
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +37 -1
- data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -1
- data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
- data/lib/doorkeeper/oauth/pre_authorization.rb +11 -5
- data/lib/doorkeeper/oauth/refresh_token_request.rb +5 -2
- data/lib/doorkeeper/oauth/scopes.rb +5 -3
- data/lib/doorkeeper/oauth/token.rb +2 -2
- data/lib/doorkeeper/oauth/token_introspection.rb +74 -8
- data/lib/doorkeeper/oauth/token_response.rb +9 -9
- data/lib/doorkeeper/oauth.rb +5 -5
- data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -13
- data/lib/doorkeeper/orm/active_record/access_token.rb +6 -13
- data/lib/doorkeeper/orm/active_record/application.rb +21 -57
- data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +10 -3
- data/lib/doorkeeper/orm/active_record.rb +6 -6
- data/lib/doorkeeper/rails/helpers.rb +1 -1
- data/lib/doorkeeper/rails/routes/mapping.rb +7 -7
- data/lib/doorkeeper/rails/routes.rb +11 -11
- data/lib/doorkeeper/rake/db.rake +13 -13
- data/lib/doorkeeper/rake.rb +1 -1
- data/lib/doorkeeper/request.rb +1 -1
- data/lib/doorkeeper/secret_storing/base.rb +64 -0
- data/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
- data/lib/doorkeeper/secret_storing/plain.rb +33 -0
- data/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
- data/lib/doorkeeper/stale_records_cleaner.rb +1 -1
- data/lib/doorkeeper/version.rb +3 -3
- data/lib/doorkeeper.rb +69 -60
- data/lib/generators/doorkeeper/application_owner_generator.rb +10 -9
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +10 -9
- data/lib/generators/doorkeeper/install_generator.rb +11 -9
- data/lib/generators/doorkeeper/migration_generator.rb +9 -9
- data/lib/generators/doorkeeper/pkce_generator.rb +10 -9
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +10 -9
- data/lib/generators/doorkeeper/templates/initializer.rb +120 -13
- data/lib/generators/doorkeeper/templates/migration.rb.erb +15 -7
- data/lib/generators/doorkeeper/views_generator.rb +6 -4
- data/spec/controllers/application_metal_controller_spec.rb +24 -10
- data/spec/controllers/applications_controller_spec.rb +54 -52
- data/spec/controllers/authorizations_controller_spec.rb +139 -145
- data/spec/controllers/protected_resources_controller_spec.rb +78 -76
- data/spec/controllers/token_info_controller_spec.rb +14 -12
- data/spec/controllers/tokens_controller_spec.rb +167 -108
- data/spec/dummy/Rakefile +3 -1
- data/spec/dummy/app/controllers/application_controller.rb +3 -1
- data/spec/dummy/app/controllers/custom_authorizations_controller.rb +2 -0
- data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -2
- data/spec/dummy/app/controllers/home_controller.rb +5 -3
- data/spec/dummy/app/controllers/metal_controller.rb +2 -0
- data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +4 -2
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/user.rb +2 -0
- data/spec/dummy/config/application.rb +25 -1
- data/spec/dummy/config/environments/development.rb +2 -0
- data/spec/dummy/config/environments/production.rb +2 -0
- data/spec/dummy/config/environments/test.rb +3 -1
- data/spec/dummy/config/initializers/backtrace_silencers.rb +2 -0
- data/spec/dummy/config/initializers/doorkeeper.rb +5 -2
- data/spec/dummy/config/initializers/secret_token.rb +3 -1
- data/spec/dummy/config/initializers/session_store.rb +3 -1
- data/spec/dummy/config/initializers/wrap_parameters.rb +2 -0
- data/spec/dummy/config.ru +3 -1
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +17 -10
- data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +2 -0
- data/spec/dummy/db/schema.rb +1 -1
- data/spec/dummy/script/rails +5 -3
- data/spec/factories.rb +8 -6
- data/spec/generators/application_owner_generator_spec.rb +13 -26
- data/spec/generators/confidential_applications_generator_spec.rb +12 -28
- data/spec/generators/install_generator_spec.rb +17 -15
- data/spec/generators/migration_generator_spec.rb +13 -26
- data/spec/generators/pkce_generator_spec.rb +11 -26
- data/spec/generators/previous_refresh_token_generator_spec.rb +16 -29
- data/spec/generators/templates/routes.rb +2 -0
- data/spec/generators/views_generator_spec.rb +14 -12
- data/spec/grape/grape_integration_spec.rb +34 -32
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +9 -7
- data/spec/lib/config_spec.rb +268 -99
- data/spec/lib/doorkeeper_spec.rb +3 -1
- data/spec/lib/models/expirable_spec.rb +22 -8
- data/spec/lib/models/reusable_spec.rb +40 -0
- data/spec/lib/models/revocable_spec.rb +8 -6
- data/spec/lib/models/scopes_spec.rb +27 -13
- data/spec/lib/models/secret_storable_spec.rb +135 -0
- data/spec/lib/oauth/authorization/uri_builder_spec.rb +17 -15
- data/spec/lib/oauth/authorization_code_request_spec.rb +34 -11
- data/spec/lib/oauth/base_request_spec.rb +20 -8
- data/spec/lib/oauth/base_response_spec.rb +3 -1
- data/spec/lib/oauth/client/credentials_spec.rb +24 -22
- data/spec/lib/oauth/client_credentials/creator_spec.rb +56 -10
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +27 -18
- data/spec/lib/oauth/client_credentials/validation_spec.rb +20 -15
- data/spec/lib/oauth/client_credentials_integration_spec.rb +7 -5
- data/spec/lib/oauth/client_credentials_request_spec.rb +27 -21
- data/spec/lib/oauth/client_spec.rb +15 -13
- data/spec/lib/oauth/code_request_spec.rb +8 -6
- data/spec/lib/oauth/code_response_spec.rb +9 -7
- data/spec/lib/oauth/error_response_spec.rb +19 -11
- data/spec/lib/oauth/error_spec.rb +4 -2
- data/spec/lib/oauth/forbidden_token_response_spec.rb +7 -5
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +68 -31
- data/spec/lib/oauth/helpers/unique_token_spec.rb +8 -6
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +115 -95
- data/spec/lib/oauth/invalid_token_response_spec.rb +3 -1
- data/spec/lib/oauth/password_access_token_request_spec.rb +79 -30
- data/spec/lib/oauth/pre_authorization_spec.rb +80 -54
- data/spec/lib/oauth/refresh_token_request_spec.rb +36 -33
- data/spec/lib/oauth/scopes_spec.rb +63 -61
- data/spec/lib/oauth/token_request_spec.rb +79 -24
- data/spec/lib/oauth/token_response_spec.rb +40 -38
- data/spec/lib/oauth/token_spec.rb +60 -44
- data/spec/lib/request/strategy_spec.rb +3 -1
- data/spec/lib/secret_storing/base_spec.rb +60 -0
- data/spec/lib/secret_storing/bcrypt_spec.rb +49 -0
- data/spec/lib/secret_storing/plain_spec.rb +44 -0
- data/spec/lib/secret_storing/sha256_hash_spec.rb +48 -0
- data/spec/lib/server_spec.rb +16 -14
- data/spec/lib/stale_records_cleaner_spec.rb +17 -17
- data/spec/models/doorkeeper/access_grant_spec.rb +80 -15
- data/spec/models/doorkeeper/access_token_spec.rb +198 -73
- data/spec/models/doorkeeper/application_spec.rb +280 -288
- data/spec/requests/applications/applications_request_spec.rb +98 -66
- data/spec/requests/applications/authorized_applications_spec.rb +20 -18
- data/spec/requests/endpoints/authorization_spec.rb +25 -23
- data/spec/requests/endpoints/token_spec.rb +38 -36
- data/spec/requests/flows/authorization_code_errors_spec.rb +26 -24
- data/spec/requests/flows/authorization_code_spec.rb +193 -151
- data/spec/requests/flows/client_credentials_spec.rb +54 -52
- data/spec/requests/flows/implicit_grant_errors_spec.rb +10 -8
- data/spec/requests/flows/implicit_grant_spec.rb +27 -25
- data/spec/requests/flows/password_spec.rb +59 -55
- data/spec/requests/flows/refresh_token_spec.rb +45 -43
- data/spec/requests/flows/revoke_token_spec.rb +40 -54
- data/spec/requests/flows/skip_authorization_spec.rb +23 -21
- data/spec/requests/protected_resources/metal_spec.rb +7 -5
- data/spec/requests/protected_resources/private_api_spec.rb +35 -33
- data/spec/routing/custom_controller_routes_spec.rb +71 -65
- data/spec/routing/default_routes_spec.rb +22 -20
- data/spec/routing/scoped_routes_spec.rb +20 -18
- data/spec/spec_helper.rb +15 -13
- data/spec/spec_helper_integration.rb +3 -1
- data/spec/support/dependencies/factory_bot.rb +3 -1
- data/spec/support/doorkeeper_rspec.rb +3 -1
- data/spec/support/helpers/access_token_request_helper.rb +3 -1
- data/spec/support/helpers/authorization_request_helper.rb +4 -2
- data/spec/support/helpers/config_helper.rb +2 -0
- data/spec/support/helpers/model_helper.rb +3 -1
- data/spec/support/helpers/request_spec_helper.rb +5 -3
- data/spec/support/helpers/url_helper.rb +9 -7
- data/spec/support/http_method_shim.rb +4 -9
- data/spec/support/orm/active_record.rb +3 -1
- data/spec/support/shared/controllers_shared_context.rb +18 -16
- data/spec/support/shared/hashing_shared_context.rb +36 -0
- data/spec/support/shared/models_shared_examples.rb +12 -10
- data/spec/validators/redirect_uri_validator_spec.rb +74 -45
- data/spec/version/version_spec.rb +7 -5
- metadata +60 -26
- data/gemfiles/rails_4_2.gemfile +0 -13
- data/spec/dummy/config/initializers/new_framework_defaults.rb +0 -8
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
2
4
|
|
|
3
5
|
module Doorkeeper::OAuth
|
|
4
6
|
describe PasswordAccessTokenRequest do
|
|
@@ -16,11 +18,15 @@ module Doorkeeper::OAuth
|
|
|
16
18
|
let(:client) { FactoryBot.create(:application) }
|
|
17
19
|
let(:owner) { double :owner, id: 99 }
|
|
18
20
|
|
|
21
|
+
before do
|
|
22
|
+
allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)
|
|
23
|
+
end
|
|
24
|
+
|
|
19
25
|
subject do
|
|
20
26
|
PasswordAccessTokenRequest.new(server, client, owner)
|
|
21
27
|
end
|
|
22
28
|
|
|
23
|
-
it
|
|
29
|
+
it "issues a new token for the client" do
|
|
24
30
|
expect do
|
|
25
31
|
subject.authorize
|
|
26
32
|
end.to change { client.reload.access_tokens.count }.by(1)
|
|
@@ -28,35 +34,35 @@ module Doorkeeper::OAuth
|
|
|
28
34
|
expect(client.reload.access_tokens.max_by(&:created_at).expires_in).to eq(1234)
|
|
29
35
|
end
|
|
30
36
|
|
|
31
|
-
it
|
|
37
|
+
it "issues a new token without a client" do
|
|
32
38
|
expect do
|
|
33
39
|
subject.client = nil
|
|
34
40
|
subject.authorize
|
|
35
41
|
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
|
36
42
|
end
|
|
37
43
|
|
|
38
|
-
it
|
|
44
|
+
it "does not issue a new token with an invalid client" do
|
|
39
45
|
expect do
|
|
40
46
|
subject.client = nil
|
|
41
|
-
subject.parameters = { client_id:
|
|
47
|
+
subject.parameters = { client_id: "bad_id" }
|
|
42
48
|
subject.authorize
|
|
43
49
|
end.not_to(change { Doorkeeper::AccessToken.count })
|
|
44
50
|
|
|
45
51
|
expect(subject.error).to eq(:invalid_client)
|
|
46
52
|
end
|
|
47
53
|
|
|
48
|
-
it
|
|
54
|
+
it "requires the owner" do
|
|
49
55
|
subject.resource_owner = nil
|
|
50
56
|
subject.validate
|
|
51
57
|
expect(subject.error).to eq(:invalid_grant)
|
|
52
58
|
end
|
|
53
59
|
|
|
54
|
-
it
|
|
60
|
+
it "optionally accepts the client" do
|
|
55
61
|
subject.client = nil
|
|
56
62
|
expect(subject).to be_valid
|
|
57
63
|
end
|
|
58
64
|
|
|
59
|
-
it
|
|
65
|
+
it "creates token even when there is already one (default)" do
|
|
60
66
|
FactoryBot.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
|
|
61
67
|
|
|
62
68
|
expect do
|
|
@@ -64,7 +70,7 @@ module Doorkeeper::OAuth
|
|
|
64
70
|
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
|
65
71
|
end
|
|
66
72
|
|
|
67
|
-
it
|
|
73
|
+
it "skips token creation if there is already one reusable" do
|
|
68
74
|
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
|
69
75
|
FactoryBot.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
|
|
70
76
|
|
|
@@ -73,6 +79,16 @@ module Doorkeeper::OAuth
|
|
|
73
79
|
end.not_to(change { Doorkeeper::AccessToken.count })
|
|
74
80
|
end
|
|
75
81
|
|
|
82
|
+
it "creates token when there is already one but non reusable" do
|
|
83
|
+
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
|
84
|
+
FactoryBot.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
|
|
85
|
+
allow_any_instance_of(Doorkeeper::AccessToken).to receive(:reusable?).and_return(false)
|
|
86
|
+
|
|
87
|
+
expect do
|
|
88
|
+
subject.authorize
|
|
89
|
+
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
|
90
|
+
end
|
|
91
|
+
|
|
76
92
|
it "calls configured request callback methods" do
|
|
77
93
|
expect(Doorkeeper.configuration.before_successful_strategy_response)
|
|
78
94
|
.to receive(:call).with(subject).once
|
|
@@ -83,28 +99,53 @@ module Doorkeeper::OAuth
|
|
|
83
99
|
subject.authorize
|
|
84
100
|
end
|
|
85
101
|
|
|
86
|
-
describe
|
|
102
|
+
describe "with scopes" do
|
|
87
103
|
subject do
|
|
88
|
-
PasswordAccessTokenRequest.new(server, client, owner, scope:
|
|
104
|
+
PasswordAccessTokenRequest.new(server, client, owner, scope: "public")
|
|
89
105
|
end
|
|
90
106
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
107
|
+
context "when scopes_by_grant_type is not configured for grant_type" do
|
|
108
|
+
it "returns error when scopes are invalid" do
|
|
109
|
+
allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string("another"))
|
|
110
|
+
subject.validate
|
|
111
|
+
expect(subject.error).to eq(:invalid_scope)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "creates the token with scopes if scopes are valid" do
|
|
115
|
+
allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string("public"))
|
|
116
|
+
expect do
|
|
117
|
+
subject.authorize
|
|
118
|
+
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
|
119
|
+
|
|
120
|
+
expect(Doorkeeper::AccessToken.last.scopes).to include("public")
|
|
121
|
+
end
|
|
95
122
|
end
|
|
96
123
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
124
|
+
context "when scopes_by_grant_type is configured for grant_type" do
|
|
125
|
+
it "returns error when scopes are valid but not permitted for grant_type" do
|
|
126
|
+
allow(server)
|
|
127
|
+
.to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string("public"))
|
|
128
|
+
allow(Doorkeeper.configuration)
|
|
129
|
+
.to receive(:scopes_by_grant_type).and_return(password: "another")
|
|
130
|
+
subject.validate
|
|
131
|
+
expect(subject.error).to eq(:invalid_scope)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "creates the token with scopes if scopes are valid and permitted for grant_type" do
|
|
135
|
+
allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string("public"))
|
|
136
|
+
allow(Doorkeeper.configuration)
|
|
137
|
+
.to receive(:scopes_by_grant_type).and_return(password: [:public])
|
|
138
|
+
|
|
139
|
+
expect do
|
|
140
|
+
subject.authorize
|
|
141
|
+
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
|
142
|
+
|
|
143
|
+
expect(Doorkeeper::AccessToken.last.scopes).to include("public")
|
|
144
|
+
end
|
|
104
145
|
end
|
|
105
146
|
end
|
|
106
147
|
|
|
107
|
-
describe
|
|
148
|
+
describe "with custom expiry" do
|
|
108
149
|
let(:server) do
|
|
109
150
|
double(
|
|
110
151
|
:server,
|
|
@@ -112,14 +153,22 @@ module Doorkeeper::OAuth
|
|
|
112
153
|
access_token_expires_in: 2.hours,
|
|
113
154
|
refresh_token_enabled?: false,
|
|
114
155
|
custom_access_token_expires_in: lambda { |context|
|
|
115
|
-
context.scopes.exists?(
|
|
156
|
+
if context.scopes.exists?("public")
|
|
157
|
+
222
|
|
158
|
+
elsif context.scopes.exists?("magic")
|
|
159
|
+
Float::INFINITY
|
|
160
|
+
end
|
|
116
161
|
}
|
|
117
162
|
)
|
|
118
163
|
end
|
|
119
164
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
165
|
+
before do
|
|
166
|
+
allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
it "checks scopes" do
|
|
170
|
+
subject = PasswordAccessTokenRequest.new(server, client, owner, scope: "public")
|
|
171
|
+
allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string("public"))
|
|
123
172
|
|
|
124
173
|
expect do
|
|
125
174
|
subject.authorize
|
|
@@ -128,9 +177,9 @@ module Doorkeeper::OAuth
|
|
|
128
177
|
expect(Doorkeeper::AccessToken.last.expires_in).to eq(222)
|
|
129
178
|
end
|
|
130
179
|
|
|
131
|
-
it
|
|
132
|
-
subject = PasswordAccessTokenRequest.new(server, client, owner, scope:
|
|
133
|
-
allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string(
|
|
180
|
+
it "falls back to the default otherwise" do
|
|
181
|
+
subject = PasswordAccessTokenRequest.new(server, client, owner, scope: "private")
|
|
182
|
+
allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string("private"))
|
|
134
183
|
|
|
135
184
|
expect do
|
|
136
185
|
subject.authorize
|
|
@@ -1,29 +1,31 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
2
4
|
|
|
3
5
|
module Doorkeeper::OAuth
|
|
4
6
|
describe PreAuthorization do
|
|
5
7
|
let(:server) do
|
|
6
8
|
server = Doorkeeper.configuration
|
|
7
9
|
allow(server).to receive(:default_scopes).and_return(Scopes.new)
|
|
8
|
-
allow(server).to receive(:scopes).and_return(Scopes.from_string(
|
|
10
|
+
allow(server).to receive(:scopes).and_return(Scopes.from_string("public profile"))
|
|
9
11
|
server
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
let(:application) do
|
|
13
15
|
application = double :application
|
|
14
|
-
allow(application).to receive(:scopes).and_return(Scopes.from_string(
|
|
16
|
+
allow(application).to receive(:scopes).and_return(Scopes.from_string(""))
|
|
15
17
|
application
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
let(:client) do
|
|
19
|
-
double :client, redirect_uri:
|
|
21
|
+
double :client, redirect_uri: "http://tst.com/auth", application: application
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
let :attributes do
|
|
23
25
|
{
|
|
24
|
-
response_type:
|
|
25
|
-
redirect_uri:
|
|
26
|
-
state:
|
|
26
|
+
response_type: "code",
|
|
27
|
+
redirect_uri: "http://tst.com/auth",
|
|
28
|
+
state: "save-this",
|
|
27
29
|
}
|
|
28
30
|
end
|
|
29
31
|
|
|
@@ -31,133 +33,157 @@ module Doorkeeper::OAuth
|
|
|
31
33
|
PreAuthorization.new(server, client, attributes)
|
|
32
34
|
end
|
|
33
35
|
|
|
34
|
-
it
|
|
36
|
+
it "is authorizable when request is valid" do
|
|
35
37
|
expect(subject).to be_authorizable
|
|
36
38
|
end
|
|
37
39
|
|
|
38
|
-
it
|
|
39
|
-
subject.response_type =
|
|
40
|
+
it "accepts code as response type" do
|
|
41
|
+
subject.response_type = "code"
|
|
40
42
|
expect(subject).to be_authorizable
|
|
41
43
|
end
|
|
42
44
|
|
|
43
|
-
it
|
|
44
|
-
allow(server).to receive(:grant_flows).and_return([
|
|
45
|
-
subject.response_type =
|
|
45
|
+
it "accepts token as response type" do
|
|
46
|
+
allow(server).to receive(:grant_flows).and_return(["implicit"])
|
|
47
|
+
subject.response_type = "token"
|
|
46
48
|
expect(subject).to be_authorizable
|
|
47
49
|
end
|
|
48
50
|
|
|
49
|
-
context
|
|
51
|
+
context "when using default grant flows" do
|
|
50
52
|
it 'accepts "code" as response type' do
|
|
51
|
-
subject.response_type =
|
|
53
|
+
subject.response_type = "code"
|
|
52
54
|
expect(subject).to be_authorizable
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
it 'accepts "token" as response type' do
|
|
56
|
-
allow(server).to receive(:grant_flows).and_return([
|
|
57
|
-
subject.response_type =
|
|
58
|
+
allow(server).to receive(:grant_flows).and_return(["implicit"])
|
|
59
|
+
subject.response_type = "token"
|
|
58
60
|
expect(subject).to be_authorizable
|
|
59
61
|
end
|
|
60
62
|
end
|
|
61
63
|
|
|
62
|
-
context
|
|
64
|
+
context "when authorization code grant flow is disabled" do
|
|
63
65
|
before do
|
|
64
|
-
allow(server).to receive(:grant_flows).and_return([
|
|
66
|
+
allow(server).to receive(:grant_flows).and_return(["implicit"])
|
|
65
67
|
end
|
|
66
68
|
|
|
67
69
|
it 'does not accept "code" as response type' do
|
|
68
|
-
subject.response_type =
|
|
70
|
+
subject.response_type = "code"
|
|
69
71
|
expect(subject).not_to be_authorizable
|
|
70
72
|
end
|
|
71
73
|
end
|
|
72
74
|
|
|
73
|
-
context
|
|
75
|
+
context "when implicit grant flow is disabled" do
|
|
74
76
|
before do
|
|
75
|
-
allow(server).to receive(:grant_flows).and_return([
|
|
77
|
+
allow(server).to receive(:grant_flows).and_return(["authorization_code"])
|
|
76
78
|
end
|
|
77
79
|
|
|
78
80
|
it 'does not accept "token" as response type' do
|
|
79
|
-
subject.response_type =
|
|
81
|
+
subject.response_type = "token"
|
|
80
82
|
expect(subject).not_to be_authorizable
|
|
81
83
|
end
|
|
82
84
|
end
|
|
83
85
|
|
|
84
|
-
context
|
|
85
|
-
it
|
|
86
|
-
subject.scope =
|
|
86
|
+
context "client application does not restrict valid scopes" do
|
|
87
|
+
it "accepts valid scopes" do
|
|
88
|
+
subject.scope = "public"
|
|
89
|
+
expect(subject).to be_authorizable
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "rejects (globally) non-valid scopes" do
|
|
93
|
+
subject.scope = "invalid"
|
|
94
|
+
expect(subject).not_to be_authorizable
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "accepts scopes which are permitted for grant_type" do
|
|
98
|
+
allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:public])
|
|
99
|
+
subject.scope = "public"
|
|
87
100
|
expect(subject).to be_authorizable
|
|
88
101
|
end
|
|
89
102
|
|
|
90
|
-
it
|
|
91
|
-
|
|
103
|
+
it "rejects scopes which are not permitted for grant_type" do
|
|
104
|
+
allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:profile])
|
|
105
|
+
subject.scope = "public"
|
|
92
106
|
expect(subject).not_to be_authorizable
|
|
93
107
|
end
|
|
94
108
|
end
|
|
95
109
|
|
|
96
|
-
context
|
|
110
|
+
context "client application restricts valid scopes" do
|
|
97
111
|
let(:application) do
|
|
98
112
|
application = double :application
|
|
99
|
-
allow(application).to receive(:scopes).and_return(Scopes.from_string(
|
|
113
|
+
allow(application).to receive(:scopes).and_return(Scopes.from_string("public nonsense"))
|
|
100
114
|
application
|
|
101
115
|
end
|
|
102
116
|
|
|
103
|
-
it
|
|
104
|
-
subject.scope =
|
|
117
|
+
it "accepts valid scopes" do
|
|
118
|
+
subject.scope = "public"
|
|
105
119
|
expect(subject).to be_authorizable
|
|
106
120
|
end
|
|
107
121
|
|
|
108
|
-
it
|
|
109
|
-
subject.scope =
|
|
122
|
+
it "rejects (globally) non-valid scopes" do
|
|
123
|
+
subject.scope = "invalid"
|
|
110
124
|
expect(subject).not_to be_authorizable
|
|
111
125
|
end
|
|
112
126
|
|
|
113
|
-
it
|
|
114
|
-
subject.scope =
|
|
127
|
+
it "rejects (application level) non-valid scopes" do
|
|
128
|
+
subject.scope = "profile"
|
|
115
129
|
expect(subject).to_not be_authorizable
|
|
116
130
|
end
|
|
131
|
+
|
|
132
|
+
it "accepts scopes which are permitted for grant_type" do
|
|
133
|
+
allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:public])
|
|
134
|
+
subject.scope = "public"
|
|
135
|
+
expect(subject).to be_authorizable
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "rejects scopes which are not permitted for grant_type" do
|
|
139
|
+
allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:profile])
|
|
140
|
+
subject.scope = "public"
|
|
141
|
+
expect(subject).not_to be_authorizable
|
|
142
|
+
end
|
|
117
143
|
end
|
|
118
144
|
|
|
119
|
-
it
|
|
120
|
-
allow(server).to receive(:default_scopes).and_return(Scopes.from_string(
|
|
145
|
+
it "uses default scopes when none is required" do
|
|
146
|
+
allow(server).to receive(:default_scopes).and_return(Scopes.from_string("default"))
|
|
121
147
|
subject.scope = nil
|
|
122
|
-
expect(subject.scope).to eq(
|
|
123
|
-
expect(subject.scopes).to eq(Scopes.from_string(
|
|
148
|
+
expect(subject.scope).to eq("default")
|
|
149
|
+
expect(subject.scopes).to eq(Scopes.from_string("default"))
|
|
124
150
|
end
|
|
125
151
|
|
|
126
|
-
context
|
|
127
|
-
let(:native_redirect_uri) {
|
|
152
|
+
context "with native redirect uri" do
|
|
153
|
+
let(:native_redirect_uri) { "urn:ietf:wg:oauth:2.0:oob" }
|
|
128
154
|
|
|
129
|
-
it
|
|
155
|
+
it "accepts redirect_uri when it matches with the client" do
|
|
130
156
|
subject.redirect_uri = native_redirect_uri
|
|
131
157
|
allow(subject.client).to receive(:redirect_uri) { native_redirect_uri }
|
|
132
158
|
expect(subject).to be_authorizable
|
|
133
159
|
end
|
|
134
160
|
|
|
135
|
-
it
|
|
136
|
-
subject.redirect_uri =
|
|
161
|
+
it "invalidates redirect_uri when it does'n match with the client" do
|
|
162
|
+
subject.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
|
|
137
163
|
expect(subject).not_to be_authorizable
|
|
138
164
|
end
|
|
139
165
|
end
|
|
140
166
|
|
|
141
|
-
it
|
|
142
|
-
subject.redirect_uri =
|
|
167
|
+
it "matches the redirect uri against client's one" do
|
|
168
|
+
subject.redirect_uri = "http://nothesame.com"
|
|
143
169
|
expect(subject).not_to be_authorizable
|
|
144
170
|
end
|
|
145
171
|
|
|
146
|
-
it
|
|
147
|
-
expect(subject.state).to eq(
|
|
172
|
+
it "stores the state" do
|
|
173
|
+
expect(subject.state).to eq("save-this")
|
|
148
174
|
end
|
|
149
175
|
|
|
150
|
-
it
|
|
151
|
-
subject.response_type =
|
|
176
|
+
it "rejects if response type is not allowed" do
|
|
177
|
+
subject.response_type = "whops"
|
|
152
178
|
expect(subject).not_to be_authorizable
|
|
153
179
|
end
|
|
154
180
|
|
|
155
|
-
it
|
|
181
|
+
it "requires an existing client" do
|
|
156
182
|
subject.client = nil
|
|
157
183
|
expect(subject).not_to be_authorizable
|
|
158
184
|
end
|
|
159
185
|
|
|
160
|
-
it
|
|
186
|
+
it "requires a redirect uri" do
|
|
161
187
|
subject.redirect_uri = nil
|
|
162
188
|
expect(subject).not_to be_authorizable
|
|
163
189
|
end
|
|
@@ -182,7 +208,7 @@ module Doorkeeper::OAuth
|
|
|
182
208
|
expect(json[:response_type]).to eq subject.response_type
|
|
183
209
|
expect(json[:scope]).to eq subject.scope
|
|
184
210
|
expect(json[:client_name]).to eq client_name
|
|
185
|
-
expect(json[:status]).to eq I18n.t(
|
|
211
|
+
expect(json[:status]).to eq I18n.t("doorkeeper.pre_authorization.status")
|
|
186
212
|
end
|
|
187
213
|
end
|
|
188
214
|
end
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
2
4
|
|
|
3
5
|
module Doorkeeper::OAuth
|
|
4
6
|
describe RefreshTokenRequest do
|
|
5
|
-
before do
|
|
6
|
-
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
|
|
7
|
-
end
|
|
8
|
-
|
|
9
7
|
let(:server) do
|
|
10
8
|
double :server,
|
|
11
|
-
access_token_expires_in: 2.minutes
|
|
12
|
-
custom_access_token_expires_in: ->(_context) { nil }
|
|
9
|
+
access_token_expires_in: 2.minutes
|
|
13
10
|
end
|
|
14
11
|
|
|
15
12
|
let(:refresh_token) do
|
|
@@ -19,21 +16,27 @@ module Doorkeeper::OAuth
|
|
|
19
16
|
let(:client) { refresh_token.application }
|
|
20
17
|
let(:credentials) { Client::Credentials.new(client.uid, client.secret) }
|
|
21
18
|
|
|
19
|
+
before do
|
|
20
|
+
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
|
|
21
|
+
allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(false)
|
|
22
|
+
end
|
|
23
|
+
|
|
22
24
|
subject { RefreshTokenRequest.new server, refresh_token, credentials }
|
|
23
25
|
|
|
24
|
-
it
|
|
26
|
+
it "issues a new token for the client" do
|
|
25
27
|
expect { subject.authorize }.to change { client.reload.access_tokens.count }.by(1)
|
|
26
28
|
# #sort_by used for MongoDB ORM extensions for valid ordering
|
|
27
29
|
expect(client.reload.access_tokens.max_by(&:created_at).expires_in).to eq(120)
|
|
28
30
|
end
|
|
29
31
|
|
|
30
|
-
it
|
|
32
|
+
it "issues a new token for the client with custom expires_in" do
|
|
31
33
|
server = double :server,
|
|
32
34
|
access_token_expires_in: 2.minutes,
|
|
33
35
|
custom_access_token_expires_in: lambda { |context|
|
|
34
36
|
context.grant_type == Doorkeeper::OAuth::REFRESH_TOKEN ? 1234 : nil
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)
|
|
37
40
|
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
|
|
38
41
|
|
|
39
42
|
RefreshTokenRequest.new(server, refresh_token, credentials).authorize
|
|
@@ -42,7 +45,7 @@ module Doorkeeper::OAuth
|
|
|
42
45
|
expect(client.reload.access_tokens.max_by(&:created_at).expires_in).to eq(1234)
|
|
43
46
|
end
|
|
44
47
|
|
|
45
|
-
it
|
|
48
|
+
it "revokes the previous token" do
|
|
46
49
|
expect { subject.authorize }.to change { refresh_token.revoked? }.from(false).to(true)
|
|
47
50
|
end
|
|
48
51
|
|
|
@@ -56,13 +59,13 @@ module Doorkeeper::OAuth
|
|
|
56
59
|
subject.authorize
|
|
57
60
|
end
|
|
58
61
|
|
|
59
|
-
it
|
|
62
|
+
it "requires the refresh token" do
|
|
60
63
|
subject.refresh_token = nil
|
|
61
64
|
subject.validate
|
|
62
65
|
expect(subject.error).to eq(:invalid_request)
|
|
63
66
|
end
|
|
64
67
|
|
|
65
|
-
it
|
|
68
|
+
it "requires credentials to be valid if provided" do
|
|
66
69
|
subject.client = nil
|
|
67
70
|
subject.validate
|
|
68
71
|
expect(subject.error).to eq(:invalid_client)
|
|
@@ -74,20 +77,20 @@ module Doorkeeper::OAuth
|
|
|
74
77
|
expect(subject.error).to eq(:invalid_grant)
|
|
75
78
|
end
|
|
76
79
|
|
|
77
|
-
it
|
|
80
|
+
it "rejects revoked tokens" do
|
|
78
81
|
refresh_token.revoke
|
|
79
82
|
subject.validate
|
|
80
83
|
expect(subject.error).to eq(:invalid_grant)
|
|
81
84
|
end
|
|
82
85
|
|
|
83
|
-
it
|
|
86
|
+
it "accepts expired tokens" do
|
|
84
87
|
refresh_token.expires_in = -1
|
|
85
88
|
refresh_token.save
|
|
86
89
|
subject.validate
|
|
87
90
|
expect(subject).to be_valid
|
|
88
91
|
end
|
|
89
92
|
|
|
90
|
-
context
|
|
93
|
+
context "refresh tokens expire on access token use" do
|
|
91
94
|
let(:server) do
|
|
92
95
|
double :server,
|
|
93
96
|
access_token_expires_in: 2.minutes,
|
|
@@ -100,16 +103,16 @@ module Doorkeeper::OAuth
|
|
|
100
103
|
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(true)
|
|
101
104
|
end
|
|
102
105
|
|
|
103
|
-
it
|
|
106
|
+
it "issues a new token for the client" do
|
|
104
107
|
expect { subject.authorize }.to change { client.reload.access_tokens.count }.by(1)
|
|
105
108
|
end
|
|
106
109
|
|
|
107
|
-
it
|
|
110
|
+
it "does not revoke the previous token" do
|
|
108
111
|
subject.authorize
|
|
109
112
|
expect(refresh_token).not_to be_revoked
|
|
110
113
|
end
|
|
111
114
|
|
|
112
|
-
it
|
|
115
|
+
it "sets the previous refresh token in the new access token" do
|
|
113
116
|
subject.authorize
|
|
114
117
|
expect(
|
|
115
118
|
# #sort_by used for MongoDB ORM extensions for valid ordering
|
|
@@ -118,53 +121,53 @@ module Doorkeeper::OAuth
|
|
|
118
121
|
end
|
|
119
122
|
end
|
|
120
123
|
|
|
121
|
-
context
|
|
124
|
+
context "clientless access tokens" do
|
|
122
125
|
let!(:refresh_token) { FactoryBot.create(:clientless_access_token, use_refresh_token: true) }
|
|
123
126
|
|
|
124
127
|
subject { RefreshTokenRequest.new server, refresh_token, nil }
|
|
125
128
|
|
|
126
|
-
it
|
|
129
|
+
it "issues a new token without a client" do
|
|
127
130
|
expect { subject.authorize }.to change { Doorkeeper::AccessToken.count }.by(1)
|
|
128
131
|
end
|
|
129
132
|
end
|
|
130
133
|
|
|
131
|
-
context
|
|
134
|
+
context "with scopes" do
|
|
132
135
|
let(:refresh_token) do
|
|
133
136
|
FactoryBot.create :access_token,
|
|
134
137
|
use_refresh_token: true,
|
|
135
|
-
scopes:
|
|
138
|
+
scopes: "public write"
|
|
136
139
|
end
|
|
137
140
|
let(:parameters) { {} }
|
|
138
141
|
subject { RefreshTokenRequest.new server, refresh_token, credentials, parameters }
|
|
139
142
|
|
|
140
|
-
it
|
|
143
|
+
it "transfers scopes from the old token to the new token" do
|
|
141
144
|
subject.authorize
|
|
142
145
|
expect(Doorkeeper::AccessToken.last.scopes).to eq(%i[public write])
|
|
143
146
|
end
|
|
144
147
|
|
|
145
|
-
it
|
|
146
|
-
parameters[:scopes] =
|
|
148
|
+
it "reduces scopes to the provided scopes" do
|
|
149
|
+
parameters[:scopes] = "public"
|
|
147
150
|
subject.authorize
|
|
148
151
|
expect(Doorkeeper::AccessToken.last.scopes).to eq(%i[public])
|
|
149
152
|
end
|
|
150
153
|
|
|
151
|
-
it
|
|
152
|
-
parameters[:scopes] =
|
|
154
|
+
it "validates that scopes are included in the original access token" do
|
|
155
|
+
parameters[:scopes] = "public update"
|
|
153
156
|
|
|
154
157
|
subject.validate
|
|
155
158
|
expect(subject.error).to eq(:invalid_scope)
|
|
156
159
|
end
|
|
157
160
|
|
|
158
|
-
it
|
|
159
|
-
parameters[:scopes] =
|
|
160
|
-
parameters[:scope] =
|
|
161
|
+
it "uses params[:scope] in favor of scopes if present (valid)" do
|
|
162
|
+
parameters[:scopes] = "public update"
|
|
163
|
+
parameters[:scope] = "public"
|
|
161
164
|
subject.authorize
|
|
162
165
|
expect(Doorkeeper::AccessToken.last.scopes).to eq(%i[public])
|
|
163
166
|
end
|
|
164
167
|
|
|
165
|
-
it
|
|
166
|
-
parameters[:scopes] =
|
|
167
|
-
parameters[:scope] =
|
|
168
|
+
it "uses params[:scope] in favor of scopes if present (invalid)" do
|
|
169
|
+
parameters[:scopes] = "public"
|
|
170
|
+
parameters[:scope] = "public update"
|
|
168
171
|
|
|
169
172
|
subject.validate
|
|
170
173
|
expect(subject.error).to eq(:invalid_scope)
|