doorkeeper 5.2.6 → 5.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.
Potentially problematic release.
This version of doorkeeper might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Appraisals +2 -2
- data/CHANGELOG.md +15 -14
- data/Gemfile +2 -2
- data/app/controllers/doorkeeper/application_controller.rb +2 -2
- data/app/controllers/doorkeeper/application_metal_controller.rb +2 -2
- data/app/controllers/doorkeeper/applications_controller.rb +3 -3
- data/app/controllers/doorkeeper/authorizations_controller.rb +2 -2
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +3 -3
- data/gemfiles/rails_5_0.gemfile +2 -2
- data/gemfiles/rails_5_1.gemfile +2 -2
- data/gemfiles/rails_5_2.gemfile +2 -2
- data/gemfiles/rails_6_0.gemfile +2 -2
- data/gemfiles/rails_master.gemfile +2 -2
- data/lib/doorkeeper.rb +2 -3
- data/lib/doorkeeper/config.rb +71 -39
- data/lib/doorkeeper/grape/helpers.rb +1 -1
- data/lib/doorkeeper/helpers/controller.rb +10 -8
- data/lib/doorkeeper/models/access_grant_mixin.rb +7 -6
- data/lib/doorkeeper/models/access_token_mixin.rb +55 -18
- data/lib/doorkeeper/models/application_mixin.rb +3 -3
- data/lib/doorkeeper/models/concerns/ownership.rb +1 -1
- data/lib/doorkeeper/models/concerns/reusable.rb +1 -1
- data/lib/doorkeeper/models/concerns/revocable.rb +0 -27
- data/lib/doorkeeper/oauth/authorization/code.rb +4 -4
- data/lib/doorkeeper/oauth/authorization/token.rb +9 -6
- data/lib/doorkeeper/oauth/authorization_code_request.rb +13 -6
- data/lib/doorkeeper/oauth/base_request.rb +8 -4
- data/lib/doorkeeper/oauth/client.rb +7 -8
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +16 -9
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +7 -7
- data/lib/doorkeeper/oauth/client_credentials/{validation.rb → validator.rb} +4 -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.rb +1 -1
- data/lib/doorkeeper/oauth/error_response.rb +5 -5
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +7 -5
- data/lib/doorkeeper/oauth/helpers/unique_token.rb +8 -5
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +1 -1
- data/lib/doorkeeper/oauth/invalid_request_response.rb +3 -3
- data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -2
- data/lib/doorkeeper/oauth/password_access_token_request.rb +3 -3
- data/lib/doorkeeper/oauth/pre_authorization.rb +7 -5
- data/lib/doorkeeper/oauth/refresh_token_request.rb +5 -5
- data/lib/doorkeeper/oauth/token.rb +2 -2
- data/lib/doorkeeper/oauth/token_introspection.rb +6 -6
- data/lib/doorkeeper/orm/active_record.rb +3 -3
- data/lib/doorkeeper/orm/active_record/access_grant.rb +4 -43
- data/lib/doorkeeper/orm/active_record/access_token.rb +4 -35
- data/lib/doorkeeper/orm/active_record/application.rb +3 -155
- data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +53 -0
- data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +47 -0
- data/lib/doorkeeper/orm/active_record/mixins/application.rb +128 -0
- data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +3 -3
- data/lib/doorkeeper/rails/helpers.rb +4 -4
- data/lib/doorkeeper/rails/routes.rb +5 -7
- data/lib/doorkeeper/rake/db.rake +3 -3
- data/lib/doorkeeper/request.rb +1 -1
- data/lib/doorkeeper/request/authorization_code.rb +3 -3
- data/lib/doorkeeper/request/client_credentials.rb +2 -2
- data/lib/doorkeeper/request/password.rb +2 -2
- data/lib/doorkeeper/request/refresh_token.rb +3 -3
- data/lib/doorkeeper/server.rb +1 -1
- data/lib/doorkeeper/stale_records_cleaner.rb +1 -1
- data/lib/doorkeeper/version.rb +2 -2
- data/lib/generators/doorkeeper/application_owner_generator.rb +1 -1
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +1 -1
- data/lib/generators/doorkeeper/migration_generator.rb +1 -1
- data/lib/generators/doorkeeper/pkce_generator.rb +1 -1
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +2 -2
- data/lib/generators/doorkeeper/templates/initializer.rb +39 -8
- data/spec/controllers/application_metal_controller_spec.rb +1 -1
- data/spec/controllers/applications_controller_spec.rb +3 -2
- data/spec/controllers/authorizations_controller_spec.rb +18 -18
- data/spec/controllers/protected_resources_controller_spec.rb +25 -17
- data/spec/controllers/token_info_controller_spec.rb +1 -1
- data/spec/controllers/tokens_controller_spec.rb +1 -1
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -3
- data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +1 -1
- data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +1 -1
- data/spec/generators/install_generator_spec.rb +1 -1
- data/spec/generators/previous_refresh_token_generator_spec.rb +2 -2
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
- data/spec/lib/config_spec.rb +61 -21
- data/spec/lib/doorkeeper_spec.rb +1 -1
- data/spec/lib/models/revocable_spec.rb +3 -3
- data/spec/lib/oauth/authorization_code_request_spec.rb +127 -125
- data/spec/lib/oauth/base_request_spec.rb +160 -158
- data/spec/lib/oauth/base_response_spec.rb +27 -29
- data/spec/lib/oauth/client/credentials_spec.rb +1 -1
- data/spec/lib/oauth/client_credentials/creator_spec.rb +42 -5
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +12 -12
- data/spec/lib/oauth/client_credentials/validation_spec.rb +4 -4
- data/spec/lib/oauth/client_credentials_integration_spec.rb +16 -18
- data/spec/lib/oauth/client_credentials_request_spec.rb +78 -80
- data/spec/lib/oauth/client_spec.rb +26 -26
- data/spec/lib/oauth/code_request_spec.rb +34 -34
- data/spec/lib/oauth/code_response_spec.rb +21 -25
- data/spec/lib/oauth/error_response_spec.rb +42 -44
- data/spec/lib/oauth/error_spec.rb +12 -14
- data/spec/lib/oauth/forbidden_token_response_spec.rb +11 -13
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +30 -18
- data/spec/lib/oauth/invalid_request_response_spec.rb +48 -50
- data/spec/lib/oauth/invalid_token_response_spec.rb +32 -34
- data/spec/lib/oauth/password_access_token_request_spec.rb +145 -147
- data/spec/lib/oauth/pre_authorization_spec.rb +159 -161
- data/spec/lib/oauth/refresh_token_request_spec.rb +138 -139
- data/spec/lib/oauth/scopes_spec.rb +104 -106
- data/spec/lib/oauth/token_request_spec.rb +115 -111
- data/spec/lib/oauth/token_response_spec.rb +71 -73
- data/spec/lib/oauth/token_spec.rb +121 -123
- data/spec/models/doorkeeper/access_grant_spec.rb +3 -5
- data/spec/models/doorkeeper/access_token_spec.rb +7 -7
- data/spec/models/doorkeeper/application_spec.rb +295 -373
- data/spec/requests/applications/applications_request_spec.rb +1 -1
- data/spec/requests/endpoints/authorization_spec.rb +5 -3
- data/spec/requests/flows/authorization_code_spec.rb +34 -22
- data/spec/requests/flows/client_credentials_spec.rb +1 -1
- data/spec/requests/flows/password_spec.rb +32 -12
- data/spec/requests/flows/refresh_token_spec.rb +19 -19
- data/spec/requests/flows/revoke_token_spec.rb +18 -12
- data/spec/spec_helper.rb +1 -4
- data/spec/support/shared/controllers_shared_context.rb +33 -23
- data/spec/validators/redirect_uri_validator_spec.rb +1 -1
- metadata +6 -5
- data/spec/support/http_method_shim.rb +0 -29
@@ -2,53 +2,51 @@
|
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
|
-
|
6
|
-
describe
|
7
|
-
|
8
|
-
|
9
|
-
end
|
5
|
+
describe Doorkeeper::OAuth::InvalidTokenResponse do
|
6
|
+
describe "#name" do
|
7
|
+
it { expect(subject.name).to eq(:invalid_token) }
|
8
|
+
end
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
describe "#status" do
|
11
|
+
it { expect(subject.status).to eq(:unauthorized) }
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
|
14
|
+
describe ".from_access_token" do
|
15
|
+
let(:response) { described_class.from_access_token(access_token) }
|
17
16
|
|
18
|
-
|
19
|
-
|
17
|
+
context "revoked" do
|
18
|
+
let(:access_token) { double(revoked?: true, expired?: true) }
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
it "sets a description" do
|
21
|
+
expect(response.description).to include("revoked")
|
22
|
+
end
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
end
|
24
|
+
it "sets the reason" do
|
25
|
+
expect(response.reason).to eq(:revoked)
|
28
26
|
end
|
27
|
+
end
|
29
28
|
|
30
|
-
|
31
|
-
|
29
|
+
context "expired" do
|
30
|
+
let(:access_token) { double(revoked?: false, expired?: true) }
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
it "sets a description" do
|
33
|
+
expect(response.description).to include("expired")
|
34
|
+
end
|
36
35
|
|
37
|
-
|
38
|
-
|
39
|
-
end
|
36
|
+
it "sets the reason" do
|
37
|
+
expect(response.reason).to eq(:expired)
|
40
38
|
end
|
39
|
+
end
|
41
40
|
|
42
|
-
|
43
|
-
|
41
|
+
context "unknown" do
|
42
|
+
let(:access_token) { double(revoked?: false, expired?: false) }
|
44
43
|
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
it "sets a description" do
|
45
|
+
expect(response.description).to include("invalid")
|
46
|
+
end
|
48
47
|
|
49
|
-
|
50
|
-
|
51
|
-
end
|
48
|
+
it "sets the reason" do
|
49
|
+
expect(response.reason).to eq(:unknown)
|
52
50
|
end
|
53
51
|
end
|
54
52
|
end
|
@@ -2,191 +2,189 @@
|
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
let(:owner) { double :owner, id: 99 }
|
5
|
+
describe Doorkeeper::OAuth::PasswordAccessTokenRequest do
|
6
|
+
let(:server) do
|
7
|
+
double(
|
8
|
+
:server,
|
9
|
+
default_scopes: Doorkeeper::OAuth::Scopes.new,
|
10
|
+
access_token_expires_in: 2.hours,
|
11
|
+
refresh_token_enabled?: false,
|
12
|
+
custom_access_token_expires_in: lambda { |context|
|
13
|
+
context.grant_type == Doorkeeper::OAuth::PASSWORD ? 1234 : nil
|
14
|
+
},
|
15
|
+
)
|
16
|
+
end
|
17
|
+
let(:client) { FactoryBot.create(:application) }
|
18
|
+
let(:owner) { double :owner, id: 99 }
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
before do
|
21
|
+
allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)
|
22
|
+
end
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
subject do
|
25
|
+
described_class.new(server, client, owner)
|
26
|
+
end
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
it "issues a new token for the client" do
|
29
|
+
expect do
|
30
|
+
subject.authorize
|
31
|
+
end.to change { client.reload.access_tokens.count }.by(1)
|
33
32
|
|
34
|
-
|
35
|
-
|
33
|
+
expect(client.reload.access_tokens.max_by(&:created_at).expires_in).to eq(1234)
|
34
|
+
end
|
36
35
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
36
|
+
it "issues a new token without a client" do
|
37
|
+
expect do
|
38
|
+
subject.client = nil
|
39
|
+
subject.authorize
|
40
|
+
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
41
|
+
end
|
43
42
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
43
|
+
it "does not issue a new token with an invalid client" do
|
44
|
+
expect do
|
45
|
+
subject.client = nil
|
46
|
+
subject.parameters = { client_id: "bad_id" }
|
47
|
+
subject.authorize
|
48
|
+
end.not_to(change { Doorkeeper::AccessToken.count })
|
50
49
|
|
51
|
-
|
52
|
-
|
50
|
+
expect(subject.error).to eq(:invalid_client)
|
51
|
+
end
|
53
52
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
53
|
+
it "requires the owner" do
|
54
|
+
subject.resource_owner = nil
|
55
|
+
subject.validate
|
56
|
+
expect(subject.error).to eq(:invalid_grant)
|
57
|
+
end
|
59
58
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
59
|
+
it "optionally accepts the client" do
|
60
|
+
subject.client = nil
|
61
|
+
expect(subject).to be_valid
|
62
|
+
end
|
64
63
|
|
65
|
-
|
66
|
-
|
64
|
+
it "creates token even when there is already one (default)" do
|
65
|
+
FactoryBot.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
|
67
66
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
67
|
+
expect do
|
68
|
+
subject.authorize
|
69
|
+
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
70
|
+
end
|
72
71
|
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
it "skips token creation if there is already one reusable" do
|
73
|
+
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
74
|
+
FactoryBot.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
|
76
75
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
76
|
+
expect do
|
77
|
+
subject.authorize
|
78
|
+
end.not_to(change { Doorkeeper::AccessToken.count })
|
79
|
+
end
|
81
80
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
81
|
+
it "creates token when there is already one but non reusable" do
|
82
|
+
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
83
|
+
FactoryBot.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
|
84
|
+
allow_any_instance_of(Doorkeeper::AccessToken).to receive(:reusable?).and_return(false)
|
86
85
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
86
|
+
expect do
|
87
|
+
subject.authorize
|
88
|
+
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
89
|
+
end
|
91
90
|
|
92
|
-
|
93
|
-
|
94
|
-
|
91
|
+
it "calls configured request callback methods" do
|
92
|
+
expect(Doorkeeper.configuration.before_successful_strategy_response)
|
93
|
+
.to receive(:call).with(subject).once
|
95
94
|
|
96
|
-
|
97
|
-
|
95
|
+
expect(Doorkeeper.configuration.after_successful_strategy_response)
|
96
|
+
.to receive(:call).with(subject, instance_of(Doorkeeper::OAuth::TokenResponse)).once
|
98
97
|
|
99
|
-
|
98
|
+
subject.authorize
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "with scopes" do
|
102
|
+
subject do
|
103
|
+
described_class.new(server, client, owner, scope: "public")
|
100
104
|
end
|
101
105
|
|
102
|
-
|
103
|
-
|
104
|
-
|
106
|
+
context "when scopes_by_grant_type is not configured for grant_type" do
|
107
|
+
it "returns error when scopes are invalid" do
|
108
|
+
allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string("another"))
|
109
|
+
subject.validate
|
110
|
+
expect(subject.error).to eq(:invalid_scope)
|
105
111
|
end
|
106
112
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
subject.
|
111
|
-
|
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
|
122
|
-
end
|
113
|
+
it "creates the token with scopes if scopes are valid" do
|
114
|
+
allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string("public"))
|
115
|
+
expect do
|
116
|
+
subject.authorize
|
117
|
+
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
123
118
|
|
124
|
-
|
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
|
119
|
+
expect(Doorkeeper::AccessToken.last.scopes).to include("public")
|
145
120
|
end
|
146
121
|
end
|
147
122
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
:
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
if context.scopes.exists?("public")
|
157
|
-
222
|
158
|
-
elsif context.scopes.exists?("magic")
|
159
|
-
Float::INFINITY
|
160
|
-
end
|
161
|
-
}
|
162
|
-
)
|
163
|
-
end
|
164
|
-
|
165
|
-
before do
|
166
|
-
allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)
|
123
|
+
context "when scopes_by_grant_type is configured for grant_type" do
|
124
|
+
it "returns error when scopes are valid but not permitted for grant_type" do
|
125
|
+
allow(server)
|
126
|
+
.to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string("public"))
|
127
|
+
allow(Doorkeeper.configuration)
|
128
|
+
.to receive(:scopes_by_grant_type).and_return(password: "another")
|
129
|
+
subject.validate
|
130
|
+
expect(subject.error).to eq(:invalid_scope)
|
167
131
|
end
|
168
132
|
|
169
|
-
it "
|
170
|
-
subject = PasswordAccessTokenRequest.new(server, client, owner, scope: "public")
|
133
|
+
it "creates the token with scopes if scopes are valid and permitted for grant_type" do
|
171
134
|
allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string("public"))
|
135
|
+
allow(Doorkeeper.configuration)
|
136
|
+
.to receive(:scopes_by_grant_type).and_return(password: [:public])
|
172
137
|
|
173
138
|
expect do
|
174
139
|
subject.authorize
|
175
140
|
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
176
141
|
|
177
|
-
expect(Doorkeeper::AccessToken.last.
|
142
|
+
expect(Doorkeeper::AccessToken.last.scopes).to include("public")
|
178
143
|
end
|
144
|
+
end
|
145
|
+
end
|
179
146
|
|
180
|
-
|
181
|
-
|
182
|
-
|
147
|
+
describe "with custom expiry" do
|
148
|
+
let(:server) do
|
149
|
+
double(
|
150
|
+
:server,
|
151
|
+
default_scopes: Doorkeeper::OAuth::Scopes.new,
|
152
|
+
access_token_expires_in: 2.hours,
|
153
|
+
refresh_token_enabled?: false,
|
154
|
+
custom_access_token_expires_in: lambda { |context|
|
155
|
+
if context.scopes.exists?("public")
|
156
|
+
222
|
157
|
+
elsif context.scopes.exists?("magic")
|
158
|
+
Float::INFINITY
|
159
|
+
end
|
160
|
+
},
|
161
|
+
)
|
162
|
+
end
|
183
163
|
|
184
|
-
|
185
|
-
|
186
|
-
|
164
|
+
before do
|
165
|
+
allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)
|
166
|
+
end
|
187
167
|
|
188
|
-
|
189
|
-
|
168
|
+
it "checks scopes" do
|
169
|
+
subject = described_class.new(server, client, owner, scope: "public")
|
170
|
+
allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string("public"))
|
171
|
+
|
172
|
+
expect do
|
173
|
+
subject.authorize
|
174
|
+
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
175
|
+
|
176
|
+
expect(Doorkeeper::AccessToken.last.expires_in).to eq(222)
|
177
|
+
end
|
178
|
+
|
179
|
+
it "falls back to the default otherwise" do
|
180
|
+
subject = described_class.new(server, client, owner, scope: "private")
|
181
|
+
allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string("private"))
|
182
|
+
|
183
|
+
expect do
|
184
|
+
subject.authorize
|
185
|
+
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
186
|
+
|
187
|
+
expect(Doorkeeper::AccessToken.last.expires_in).to eq(2.hours)
|
190
188
|
end
|
191
189
|
end
|
192
190
|
end
|
@@ -2,223 +2,221 @@
|
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
5
|
+
describe Doorkeeper::OAuth::PreAuthorization do
|
6
|
+
let(:server) do
|
7
|
+
server = Doorkeeper.configuration
|
8
|
+
allow(server).to receive(:default_scopes).and_return(Doorkeeper::OAuth::Scopes.from_string("default"))
|
9
|
+
allow(server).to receive(:optional_scopes).and_return(Doorkeeper::OAuth::Scopes.from_string("public profile"))
|
10
|
+
server
|
11
|
+
end
|
13
12
|
|
14
|
-
|
15
|
-
|
13
|
+
let(:application) { FactoryBot.create(:application, redirect_uri: "https://app.com/callback") }
|
14
|
+
let(:client) { Doorkeeper::OAuth::Client.find(application.uid) }
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
let :attributes do
|
17
|
+
{
|
18
|
+
client_id: client.uid,
|
19
|
+
response_type: "code",
|
20
|
+
redirect_uri: "https://app.com/callback",
|
21
|
+
state: "save-this",
|
22
|
+
}
|
23
|
+
end
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
subject do
|
26
|
+
described_class.new(server, attributes)
|
27
|
+
end
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
it "is authorizable when request is valid" do
|
30
|
+
expect(subject).to be_authorizable
|
31
|
+
end
|
32
|
+
|
33
|
+
it "accepts code as response type" do
|
34
|
+
attributes[:response_type] = "code"
|
35
|
+
expect(subject).to be_authorizable
|
36
|
+
end
|
37
|
+
|
38
|
+
it "accepts token as response type" do
|
39
|
+
allow(server).to receive(:grant_flows).and_return(["implicit"])
|
40
|
+
attributes[:response_type] = "token"
|
41
|
+
expect(subject).to be_authorizable
|
42
|
+
end
|
33
43
|
|
34
|
-
|
44
|
+
context "when using default grant flows" do
|
45
|
+
it 'accepts "code" as response type' do
|
35
46
|
attributes[:response_type] = "code"
|
36
47
|
expect(subject).to be_authorizable
|
37
48
|
end
|
38
49
|
|
39
|
-
it
|
50
|
+
it 'accepts "token" as response type' do
|
40
51
|
allow(server).to receive(:grant_flows).and_return(["implicit"])
|
41
52
|
attributes[:response_type] = "token"
|
42
53
|
expect(subject).to be_authorizable
|
43
54
|
end
|
55
|
+
end
|
44
56
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
57
|
+
context "when authorization code grant flow is disabled" do
|
58
|
+
before do
|
59
|
+
allow(server).to receive(:grant_flows).and_return(["implicit"])
|
60
|
+
end
|
50
61
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
expect(subject).to be_authorizable
|
55
|
-
end
|
62
|
+
it 'does not accept "code" as response type' do
|
63
|
+
attributes[:response_type] = "code"
|
64
|
+
expect(subject).not_to be_authorizable
|
56
65
|
end
|
66
|
+
end
|
57
67
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
68
|
+
context "when implicit grant flow is disabled" do
|
69
|
+
before do
|
70
|
+
allow(server).to receive(:grant_flows).and_return(["authorization_code"])
|
71
|
+
end
|
62
72
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
end
|
73
|
+
it 'does not accept "token" as response type' do
|
74
|
+
attributes[:response_type] = "token"
|
75
|
+
expect(subject).not_to be_authorizable
|
67
76
|
end
|
77
|
+
end
|
68
78
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
79
|
+
context "client application does not restrict valid scopes" do
|
80
|
+
it "accepts valid scopes" do
|
81
|
+
attributes[:scope] = "public"
|
82
|
+
expect(subject).to be_authorizable
|
83
|
+
end
|
73
84
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end
|
85
|
+
it "rejects (globally) non-valid scopes" do
|
86
|
+
attributes[:scope] = "invalid"
|
87
|
+
expect(subject).not_to be_authorizable
|
78
88
|
end
|
79
89
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
90
|
+
it "accepts scopes which are permitted for grant_type" do
|
91
|
+
allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:public])
|
92
|
+
attributes[:scope] = "public"
|
93
|
+
expect(subject).to be_authorizable
|
94
|
+
end
|
85
95
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
96
|
+
it "rejects scopes which are not permitted for grant_type" do
|
97
|
+
allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:profile])
|
98
|
+
attributes[:scope] = "public"
|
99
|
+
expect(subject).not_to be_authorizable
|
100
|
+
end
|
101
|
+
end
|
90
102
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
end
|
103
|
+
context "client application restricts valid scopes" do
|
104
|
+
let(:application) do
|
105
|
+
FactoryBot.create(:application, scopes: Doorkeeper::OAuth::Scopes.from_string("public nonsense"))
|
106
|
+
end
|
96
107
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
expect(subject).not_to be_authorizable
|
101
|
-
end
|
108
|
+
it "accepts valid scopes" do
|
109
|
+
attributes[:scope] = "public"
|
110
|
+
expect(subject).to be_authorizable
|
102
111
|
end
|
103
112
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
113
|
+
it "rejects (globally) non-valid scopes" do
|
114
|
+
attributes[:scope] = "invalid"
|
115
|
+
expect(subject).not_to be_authorizable
|
116
|
+
end
|
108
117
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
118
|
+
it "rejects (application level) non-valid scopes" do
|
119
|
+
attributes[:scope] = "profile"
|
120
|
+
expect(subject).to_not be_authorizable
|
121
|
+
end
|
113
122
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
123
|
+
it "accepts scopes which are permitted for grant_type" do
|
124
|
+
allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:public])
|
125
|
+
attributes[:scope] = "public"
|
126
|
+
expect(subject).to be_authorizable
|
127
|
+
end
|
118
128
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
129
|
+
it "rejects scopes which are not permitted for grant_type" do
|
130
|
+
allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:profile])
|
131
|
+
attributes[:scope] = "public"
|
132
|
+
expect(subject).not_to be_authorizable
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context "when scope is not provided to pre_authorization" do
|
137
|
+
before { attributes[:scope] = nil }
|
123
138
|
|
124
|
-
|
125
|
-
|
126
|
-
|
139
|
+
context "when default scopes is provided" do
|
140
|
+
it "uses default scopes" do
|
141
|
+
allow(server).to receive(:default_scopes).and_return(Doorkeeper::OAuth::Scopes.from_string("default_scope"))
|
127
142
|
expect(subject).to be_authorizable
|
143
|
+
expect(subject.scope).to eq("default_scope")
|
144
|
+
expect(subject.scopes).to eq(Doorkeeper::OAuth::Scopes.from_string("default_scope"))
|
128
145
|
end
|
146
|
+
end
|
129
147
|
|
130
|
-
|
131
|
-
|
132
|
-
|
148
|
+
context "when default scopes is none" do
|
149
|
+
it "not be authorizable when none default scope" do
|
150
|
+
allow(server).to receive(:default_scopes).and_return(Doorkeeper::OAuth::Scopes.new)
|
133
151
|
expect(subject).not_to be_authorizable
|
134
152
|
end
|
135
153
|
end
|
154
|
+
end
|
136
155
|
|
137
|
-
|
138
|
-
|
156
|
+
it "matches the redirect uri against client's one" do
|
157
|
+
attributes[:redirect_uri] = "http://nothesame.com"
|
158
|
+
expect(subject).not_to be_authorizable
|
159
|
+
end
|
139
160
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
expect(subject).to be_authorizable
|
144
|
-
expect(subject.scope).to eq("default_scope")
|
145
|
-
expect(subject.scopes).to eq(Scopes.from_string("default_scope"))
|
146
|
-
end
|
147
|
-
end
|
161
|
+
it "stores the state" do
|
162
|
+
expect(subject.state).to eq("save-this")
|
163
|
+
end
|
148
164
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
165
|
+
it "rejects if response type is not allowed" do
|
166
|
+
attributes[:response_type] = "whops"
|
167
|
+
expect(subject).not_to be_authorizable
|
168
|
+
end
|
156
169
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
170
|
+
it "requires an existing client" do
|
171
|
+
attributes[:client_id] = nil
|
172
|
+
expect(subject).not_to be_authorizable
|
173
|
+
end
|
161
174
|
|
162
|
-
|
163
|
-
|
164
|
-
|
175
|
+
it "requires a redirect uri" do
|
176
|
+
attributes[:redirect_uri] = nil
|
177
|
+
expect(subject).not_to be_authorizable
|
178
|
+
end
|
165
179
|
|
166
|
-
|
167
|
-
|
168
|
-
expect(subject).not_to be_authorizable
|
169
|
-
end
|
180
|
+
describe "as_json" do
|
181
|
+
before { subject.authorizable? }
|
170
182
|
|
171
|
-
it
|
172
|
-
attributes[:client_id] = nil
|
173
|
-
expect(subject).not_to be_authorizable
|
174
|
-
end
|
183
|
+
it { is_expected.to respond_to :as_json }
|
175
184
|
|
176
|
-
|
177
|
-
|
178
|
-
|
185
|
+
shared_examples "returns the pre authorization" do
|
186
|
+
it "returns the pre authorization" do
|
187
|
+
expect(json[:client_id]).to eq client.uid
|
188
|
+
expect(json[:redirect_uri]).to eq subject.redirect_uri
|
189
|
+
expect(json[:state]).to eq subject.state
|
190
|
+
expect(json[:response_type]).to eq subject.response_type
|
191
|
+
expect(json[:scope]).to eq subject.scope
|
192
|
+
expect(json[:client_name]).to eq client.name
|
193
|
+
expect(json[:status]).to eq I18n.t("doorkeeper.pre_authorization.status")
|
194
|
+
end
|
179
195
|
end
|
180
196
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
it { is_expected.to respond_to :as_json }
|
197
|
+
context "when attributes param is not passed" do
|
198
|
+
let(:json) { subject.as_json }
|
185
199
|
|
186
|
-
|
187
|
-
|
188
|
-
expect(json[:client_id]).to eq client.uid
|
189
|
-
expect(json[:redirect_uri]).to eq subject.redirect_uri
|
190
|
-
expect(json[:state]).to eq subject.state
|
191
|
-
expect(json[:response_type]).to eq subject.response_type
|
192
|
-
expect(json[:scope]).to eq subject.scope
|
193
|
-
expect(json[:client_name]).to eq client.name
|
194
|
-
expect(json[:status]).to eq I18n.t("doorkeeper.pre_authorization.status")
|
195
|
-
end
|
196
|
-
end
|
200
|
+
include_examples "returns the pre authorization"
|
201
|
+
end
|
197
202
|
|
198
|
-
|
199
|
-
|
203
|
+
context "when attributes param is passed" do
|
204
|
+
context "when attributes is a hash" do
|
205
|
+
let(:custom_attributes) { { custom_id: "1234", custom_name: "a pretty good name" } }
|
206
|
+
let(:json) { subject.as_json(custom_attributes) }
|
200
207
|
|
201
208
|
include_examples "returns the pre authorization"
|
202
|
-
end
|
203
|
-
|
204
|
-
context "when attributes param is passed" do
|
205
|
-
context "when attributes is a hash" do
|
206
|
-
let(:custom_attributes) { { custom_id: "1234", custom_name: "a pretty good name" } }
|
207
|
-
let(:json) { subject.as_json(custom_attributes) }
|
208
|
-
|
209
|
-
include_examples "returns the pre authorization"
|
210
209
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
end
|
210
|
+
it "merges the attributes in params" do
|
211
|
+
expect(json[:custom_id]).to eq custom_attributes[:custom_id]
|
212
|
+
expect(json[:custom_name]).to eq custom_attributes[:custom_name]
|
215
213
|
end
|
214
|
+
end
|
216
215
|
|
217
|
-
|
218
|
-
|
216
|
+
context "when attributes is not a hash" do
|
217
|
+
let(:json) { subject.as_json(nil) }
|
219
218
|
|
220
|
-
|
221
|
-
end
|
219
|
+
include_examples "returns the pre authorization"
|
222
220
|
end
|
223
221
|
end
|
224
222
|
end
|