doorkeeper 5.2.0.rc1 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of doorkeeper might be problematic. Click here for more details.

Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +1 -1
  3. data/CHANGELOG.md +33 -2
  4. data/CONTRIBUTING.md +7 -0
  5. data/Dangerfile +1 -1
  6. data/Dockerfile +29 -0
  7. data/Gemfile +1 -1
  8. data/README.md +9 -1
  9. data/app/controllers/doorkeeper/application_controller.rb +1 -1
  10. data/app/controllers/doorkeeper/application_metal_controller.rb +2 -1
  11. data/app/controllers/doorkeeper/authorizations_controller.rb +14 -7
  12. data/app/controllers/doorkeeper/tokens_controller.rb +14 -1
  13. data/config/locales/en.yml +5 -1
  14. data/doorkeeper.gemspec +8 -0
  15. data/gemfiles/rails_6_0.gemfile +1 -1
  16. data/lib/doorkeeper/config.rb +64 -9
  17. data/lib/doorkeeper/errors.rb +13 -18
  18. data/lib/doorkeeper/helpers/controller.rb +6 -2
  19. data/lib/doorkeeper/models/access_token_mixin.rb +43 -2
  20. data/lib/doorkeeper/oauth/authorization/code.rb +1 -5
  21. data/lib/doorkeeper/oauth/authorization_code_request.rb +18 -9
  22. data/lib/doorkeeper/oauth/base_request.rb +2 -0
  23. data/lib/doorkeeper/oauth/client_credentials/creator.rb +14 -0
  24. data/lib/doorkeeper/oauth/client_credentials/validation.rb +8 -0
  25. data/lib/doorkeeper/oauth/code_request.rb +5 -11
  26. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  27. data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
  28. data/lib/doorkeeper/oauth/pre_authorization.rb +70 -37
  29. data/lib/doorkeeper/oauth/refresh_token_request.rb +5 -2
  30. data/lib/doorkeeper/oauth/token_introspection.rb +16 -7
  31. data/lib/doorkeeper/oauth/token_request.rb +4 -18
  32. data/lib/doorkeeper/orm/active_record/application.rb +1 -1
  33. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +61 -0
  34. data/lib/doorkeeper/orm/active_record.rb +2 -2
  35. data/lib/doorkeeper/request/authorization_code.rb +2 -0
  36. data/lib/doorkeeper/request.rb +6 -11
  37. data/lib/doorkeeper/server.rb +2 -6
  38. data/lib/doorkeeper/version.rb +1 -1
  39. data/lib/doorkeeper.rb +1 -0
  40. data/lib/generators/doorkeeper/templates/initializer.rb +88 -43
  41. data/lib/generators/doorkeeper/templates/migration.rb.erb +1 -1
  42. data/spec/controllers/authorizations_controller_spec.rb +140 -61
  43. data/spec/controllers/protected_resources_controller_spec.rb +3 -3
  44. data/spec/controllers/tokens_controller_spec.rb +140 -40
  45. data/spec/dummy/config/initializers/doorkeeper.rb +47 -20
  46. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +1 -1
  47. data/spec/lib/config_spec.rb +32 -1
  48. data/spec/lib/oauth/authorization_code_request_spec.rb +11 -1
  49. data/spec/lib/oauth/base_request_spec.rb +33 -16
  50. data/spec/lib/oauth/client_credentials/creator_spec.rb +3 -0
  51. data/spec/lib/oauth/code_request_spec.rb +27 -28
  52. data/spec/lib/oauth/invalid_request_response_spec.rb +75 -0
  53. data/spec/lib/oauth/pre_authorization_spec.rb +80 -55
  54. data/spec/lib/oauth/refresh_token_request_spec.rb +1 -0
  55. data/spec/lib/oauth/token_request_spec.rb +20 -17
  56. data/spec/lib/server_spec.rb +0 -12
  57. data/spec/requests/endpoints/authorization_spec.rb +21 -5
  58. data/spec/requests/endpoints/token_spec.rb +1 -1
  59. data/spec/requests/flows/authorization_code_errors_spec.rb +1 -0
  60. data/spec/requests/flows/authorization_code_spec.rb +77 -23
  61. data/spec/requests/flows/client_credentials_spec.rb +38 -0
  62. data/spec/requests/flows/implicit_grant_errors_spec.rb +22 -10
  63. data/spec/requests/flows/implicit_grant_spec.rb +9 -8
  64. data/spec/requests/flows/password_spec.rb +37 -0
  65. data/spec/requests/flows/refresh_token_spec.rb +1 -1
  66. data/spec/support/helpers/request_spec_helper.rb +14 -2
  67. data/spec/validators/redirect_uri_validator_spec.rb +1 -1
  68. metadata +15 -6
  69. data/app/validators/redirect_uri_validator.rb +0 -60
@@ -6,31 +6,25 @@ module Doorkeeper::OAuth
6
6
  describe PreAuthorization do
7
7
  let(:server) do
8
8
  server = Doorkeeper.configuration
9
- allow(server).to receive(:default_scopes).and_return(Scopes.new)
10
- allow(server).to receive(:scopes).and_return(Scopes.from_string("public profile"))
9
+ allow(server).to receive(:default_scopes).and_return(Scopes.from_string("default"))
10
+ allow(server).to receive(:optional_scopes).and_return(Scopes.from_string("public profile"))
11
11
  server
12
12
  end
13
13
 
14
- let(:application) do
15
- application = double :application
16
- allow(application).to receive(:scopes).and_return(Scopes.from_string(""))
17
- application
18
- end
19
-
20
- let(:client) do
21
- double :client, redirect_uri: "http://tst.com/auth", application: application
22
- end
14
+ let(:application) { FactoryBot.create(:application, redirect_uri: "https://app.com/callback") }
15
+ let(:client) { Client.find(application.uid) }
23
16
 
24
17
  let :attributes do
25
18
  {
19
+ client_id: client.uid,
26
20
  response_type: "code",
27
- redirect_uri: "http://tst.com/auth",
21
+ redirect_uri: "https://app.com/callback",
28
22
  state: "save-this",
29
23
  }
30
24
  end
31
25
 
32
26
  subject do
33
- PreAuthorization.new(server, client, attributes)
27
+ PreAuthorization.new(server, attributes)
34
28
  end
35
29
 
36
30
  it "is authorizable when request is valid" do
@@ -38,25 +32,25 @@ module Doorkeeper::OAuth
38
32
  end
39
33
 
40
34
  it "accepts code as response type" do
41
- subject.response_type = "code"
35
+ attributes[:response_type] = "code"
42
36
  expect(subject).to be_authorizable
43
37
  end
44
38
 
45
39
  it "accepts token as response type" do
46
40
  allow(server).to receive(:grant_flows).and_return(["implicit"])
47
- subject.response_type = "token"
41
+ attributes[:response_type] = "token"
48
42
  expect(subject).to be_authorizable
49
43
  end
50
44
 
51
45
  context "when using default grant flows" do
52
46
  it 'accepts "code" as response type' do
53
- subject.response_type = "code"
47
+ attributes[:response_type] = "code"
54
48
  expect(subject).to be_authorizable
55
49
  end
56
50
 
57
51
  it 'accepts "token" as response type' do
58
52
  allow(server).to receive(:grant_flows).and_return(["implicit"])
59
- subject.response_type = "token"
53
+ attributes[:response_type] = "token"
60
54
  expect(subject).to be_authorizable
61
55
  end
62
56
  end
@@ -67,7 +61,7 @@ module Doorkeeper::OAuth
67
61
  end
68
62
 
69
63
  it 'does not accept "code" as response type' do
70
- subject.response_type = "code"
64
+ attributes[:response_type] = "code"
71
65
  expect(subject).not_to be_authorizable
72
66
  end
73
67
  end
@@ -78,79 +72,90 @@ module Doorkeeper::OAuth
78
72
  end
79
73
 
80
74
  it 'does not accept "token" as response type' do
81
- subject.response_type = "token"
75
+ attributes[:response_type] = "token"
82
76
  expect(subject).not_to be_authorizable
83
77
  end
84
78
  end
85
79
 
86
80
  context "client application does not restrict valid scopes" do
87
81
  it "accepts valid scopes" do
88
- subject.scope = "public"
82
+ attributes[:scope] = "public"
89
83
  expect(subject).to be_authorizable
90
84
  end
91
85
 
92
86
  it "rejects (globally) non-valid scopes" do
93
- subject.scope = "invalid"
87
+ attributes[:scope] = "invalid"
94
88
  expect(subject).not_to be_authorizable
95
89
  end
96
90
 
97
91
  it "accepts scopes which are permitted for grant_type" do
98
92
  allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:public])
99
- subject.scope = "public"
93
+ attributes[:scope] = "public"
100
94
  expect(subject).to be_authorizable
101
95
  end
102
96
 
103
97
  it "rejects scopes which are not permitted for grant_type" do
104
98
  allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:profile])
105
- subject.scope = "public"
99
+ attributes[:scope] = "public"
106
100
  expect(subject).not_to be_authorizable
107
101
  end
108
102
  end
109
103
 
110
104
  context "client application restricts valid scopes" do
111
105
  let(:application) do
112
- application = double :application
113
- allow(application).to receive(:scopes).and_return(Scopes.from_string("public nonsense"))
114
- application
106
+ FactoryBot.create(:application, scopes: Scopes.from_string("public nonsense"))
115
107
  end
116
108
 
117
109
  it "accepts valid scopes" do
118
- subject.scope = "public"
110
+ attributes[:scope] = "public"
119
111
  expect(subject).to be_authorizable
120
112
  end
121
113
 
122
114
  it "rejects (globally) non-valid scopes" do
123
- subject.scope = "invalid"
115
+ attributes[:scope] = "invalid"
124
116
  expect(subject).not_to be_authorizable
125
117
  end
126
118
 
127
119
  it "rejects (application level) non-valid scopes" do
128
- subject.scope = "profile"
120
+ attributes[:scope] = "profile"
129
121
  expect(subject).to_not be_authorizable
130
122
  end
131
123
 
132
124
  it "accepts scopes which are permitted for grant_type" do
133
125
  allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:public])
134
- subject.scope = "public"
126
+ attributes[:scope] = "public"
135
127
  expect(subject).to be_authorizable
136
128
  end
137
129
 
138
130
  it "rejects scopes which are not permitted for grant_type" do
139
131
  allow(server).to receive(:scopes_by_grant_type).and_return(authorization_code: [:profile])
140
- subject.scope = "public"
132
+ attributes[:scope] = "public"
141
133
  expect(subject).not_to be_authorizable
142
134
  end
143
135
  end
144
136
 
145
- it "uses default scopes when none is required" do
146
- allow(server).to receive(:default_scopes).and_return(Scopes.from_string("default"))
147
- subject.scope = nil
148
- expect(subject.scope).to eq("default")
149
- expect(subject.scopes).to eq(Scopes.from_string("default"))
137
+ context "when scope is not provided to pre_authorization" do
138
+ before { attributes[:scope] = nil }
139
+
140
+ context "when default scopes is provided" do
141
+ it "uses default scopes" do
142
+ allow(server).to receive(:default_scopes).and_return(Scopes.from_string("default_scope"))
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
148
+
149
+ context "when default scopes is none" do
150
+ it "not be authorizable when none default scope" do
151
+ allow(server).to receive(:default_scopes).and_return(Scopes.new)
152
+ expect(subject).not_to be_authorizable
153
+ end
154
+ end
150
155
  end
151
156
 
152
157
  it "matches the redirect uri against client's one" do
153
- subject.redirect_uri = "http://nothesame.com"
158
+ attributes[:redirect_uri] = "http://nothesame.com"
154
159
  expect(subject).not_to be_authorizable
155
160
  end
156
161
 
@@ -159,41 +164,61 @@ module Doorkeeper::OAuth
159
164
  end
160
165
 
161
166
  it "rejects if response type is not allowed" do
162
- subject.response_type = "whops"
167
+ attributes[:response_type] = "whops"
163
168
  expect(subject).not_to be_authorizable
164
169
  end
165
170
 
166
171
  it "requires an existing client" do
167
- subject.client = nil
172
+ attributes[:client_id] = nil
168
173
  expect(subject).not_to be_authorizable
169
174
  end
170
175
 
171
176
  it "requires a redirect uri" do
172
- subject.redirect_uri = nil
177
+ attributes[:redirect_uri] = nil
173
178
  expect(subject).not_to be_authorizable
174
179
  end
175
180
 
176
181
  describe "as_json" do
177
- let(:client_id) { "client_uid_123" }
178
- let(:client_name) { "Acme Co." }
182
+ before { subject.authorizable? }
179
183
 
180
- before do
181
- allow(client).to receive(:uid).and_return client_id
182
- allow(client).to receive(:name).and_return client_name
184
+ it { is_expected.to respond_to :as_json }
185
+
186
+ shared_examples "returns the pre authorization" do
187
+ it "returns the pre authorization" do
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
183
196
  end
184
197
 
185
- let(:json) { subject.as_json({}) }
198
+ context "when attributes param is not passed" do
199
+ let(:json) { subject.as_json }
186
200
 
187
- it { is_expected.to respond_to :as_json }
201
+ 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
+
211
+ it "merges the attributes in params" do
212
+ expect(json[:custom_id]).to eq custom_attributes[:custom_id]
213
+ expect(json[:custom_name]).to eq custom_attributes[:custom_name]
214
+ end
215
+ end
216
+
217
+ context "when attributes is not a hash" do
218
+ let(:json) { subject.as_json(nil) }
188
219
 
189
- it "returns correct values" do
190
- expect(json[:client_id]).to eq client_id
191
- expect(json[:redirect_uri]).to eq subject.redirect_uri
192
- expect(json[:state]).to eq subject.state
193
- expect(json[:response_type]).to eq subject.response_type
194
- expect(json[:scope]).to eq subject.scope
195
- expect(json[:client_name]).to eq client_name
196
- expect(json[:status]).to eq I18n.t("doorkeeper.pre_authorization.status")
220
+ include_examples "returns the pre authorization"
221
+ end
197
222
  end
198
223
  end
199
224
  end
@@ -63,6 +63,7 @@ module Doorkeeper::OAuth
63
63
  subject.refresh_token = nil
64
64
  subject.validate
65
65
  expect(subject.error).to eq(:invalid_request)
66
+ expect(subject.missing_param).to eq(:refresh_token)
66
67
  end
67
68
 
68
69
  it "requires credentials to be valid if provided" do
@@ -9,15 +9,21 @@ module Doorkeeper::OAuth
9
9
  end
10
10
 
11
11
  let :pre_auth do
12
- double(
13
- :pre_auth,
14
- client: application,
15
- redirect_uri: "http://tst.com/cb",
16
- state: nil,
17
- scopes: Scopes.from_string("public"),
18
- error: nil,
19
- authorizable?: true
20
- )
12
+ server = Doorkeeper.configuration
13
+ allow(server).to receive(:default_scopes).and_return(Scopes.from_string("public"))
14
+ allow(server).to receive(:grant_flows).and_return(Scopes.from_string("implicit"))
15
+
16
+ client = Doorkeeper::OAuth::Client.new(application)
17
+
18
+ attributes = {
19
+ client_id: client.uid,
20
+ response_type: "token",
21
+ redirect_uri: "https://app.com/callback",
22
+ }
23
+
24
+ pre_auth = PreAuthorization.new(server, attributes)
25
+ pre_auth.authorizable?
26
+ pre_auth
21
27
  end
22
28
 
23
29
  let :owner do
@@ -38,14 +44,11 @@ module Doorkeeper::OAuth
38
44
  expect(subject.authorize).to be_a(CodeResponse)
39
45
  end
40
46
 
41
- it "does not create token when not authorizable" do
42
- allow(pre_auth).to receive(:authorizable?).and_return(false)
43
- expect { subject.authorize }.not_to(change { Doorkeeper::AccessToken.count })
44
- end
45
-
46
- it "returns a error response" do
47
- allow(pre_auth).to receive(:authorizable?).and_return(false)
48
- expect(subject.authorize).to be_a(ErrorResponse)
47
+ context "when pre_auth is denied" do
48
+ it "does not create token and returns a error response" do
49
+ expect { subject.deny }.not_to(change { Doorkeeper::AccessToken.count })
50
+ expect(subject.deny).to be_a(ErrorResponse)
51
+ end
49
52
  end
50
53
 
51
54
  describe "with custom expiration" do
@@ -10,12 +10,6 @@ describe Doorkeeper::Server do
10
10
  end
11
11
 
12
12
  describe ".authorization_request" do
13
- it "raises error when strategy does not exist" do
14
- expect do
15
- subject.authorization_request(:duh)
16
- end.to raise_error(Doorkeeper::Errors::InvalidAuthorizationStrategy)
17
- end
18
-
19
13
  it "raises error when strategy does not match phase" do
20
14
  expect do
21
15
  subject.token_request(:code)
@@ -29,12 +23,6 @@ describe Doorkeeper::Server do
29
23
  .and_return(["authorization_code"])
30
24
  end
31
25
 
32
- it "raises error when using the disabled Implicit strategy" do
33
- expect do
34
- subject.authorization_request(:token)
35
- end.to raise_error(Doorkeeper::Errors::InvalidAuthorizationStrategy)
36
- end
37
-
38
26
  it "raises error when using the disabled Client Credentials strategy" do
39
27
  expect do
40
28
  subject.token_request(:client_credentials)
@@ -4,6 +4,7 @@ require "spec_helper"
4
4
 
5
5
  feature "Authorization endpoint" do
6
6
  background do
7
+ default_scopes_exist :default
7
8
  config_is_set(:authenticate_resource_owner) { User.first || redirect_to("/sign_in") }
8
9
  client_exists(name: "MyApp")
9
10
  end
@@ -34,16 +35,31 @@ feature "Authorization endpoint" do
34
35
  end
35
36
  end
36
37
 
37
- context "with a invalid request" do
38
+ context "with a invalid request's param" do
38
39
  background do
39
40
  create_resource_owner
40
41
  sign_in
41
42
  end
42
43
 
43
- scenario "displays the related error" do
44
- visit authorization_endpoint_url(client: @client, response_type: "")
45
- i_should_not_see "Authorize"
46
- i_should_see_translated_error_message :unsupported_response_type
44
+ context "when missing required param" do
45
+ scenario "displays invalid_request error when missing client" do
46
+ visit authorization_endpoint_url(client: nil, response_type: "code")
47
+ i_should_not_see "Authorize"
48
+ i_should_see_translated_invalid_request_error_message :missing_param, :client_id
49
+ end
50
+
51
+ scenario "displays invalid_request error when missing response_type param" do
52
+ visit authorization_endpoint_url(client: @client, response_type: "")
53
+ i_should_not_see "Authorize"
54
+ i_should_see_translated_invalid_request_error_message :missing_param, :response_type
55
+ end
56
+
57
+ scenario "displays invalid_request error when missing scope param and authorization server has no default scopes" do
58
+ config_is_set(:default_scopes, [])
59
+ visit authorization_endpoint_url(client: @client, response_type: "code", scope: "")
60
+ i_should_not_see "Authorize"
61
+ i_should_see_translated_invalid_request_error_message :missing_param, :scope
62
+ end
47
63
  end
48
64
 
49
65
  scenario "displays unsupported_response_type error when using a disabled response type" do
@@ -70,6 +70,6 @@ describe "Token endpoint" do
70
70
 
71
71
  should_not_have_json "access_token"
72
72
  should_have_json "error", "invalid_request"
73
- should_have_json "error_description", translated_error_message("invalid_request")
73
+ should_have_json "error_description", translated_invalid_request_error_message(:missing_param, :grant_type)
74
74
  end
75
75
  end
@@ -5,6 +5,7 @@ require "spec_helper"
5
5
  feature "Authorization Code Flow Errors" do
6
6
  let(:client_params) { {} }
7
7
  background do
8
+ default_scopes_exist :default
8
9
  config_is_set(:authenticate_resource_owner) { User.first || redirect_to("/sign_in") }
9
10
  client_exists client_params
10
11
  create_resource_owner
@@ -4,6 +4,7 @@ require "spec_helper"
4
4
 
5
5
  feature "Authorization Code Flow" do
6
6
  background do
7
+ default_scopes_exist :default
7
8
  config_is_set(:authenticate_resource_owner) { User.first || redirect_to("/sign_in") }
8
9
  client_exists
9
10
  create_resource_owner
@@ -23,6 +24,39 @@ feature "Authorization Code Flow" do
23
24
  url_should_not_have_param("error")
24
25
  end
25
26
 
27
+ context "when configured to check application supported grant flow" do
28
+ before do
29
+ config_is_set(:allow_grant_flow_for_client, ->(_grant_flow, client) { client.name == "admin" })
30
+ end
31
+
32
+ scenario "forbids the request when doesn't satisfy condition" do
33
+ @client.update(name: "sample app")
34
+
35
+ visit authorization_endpoint_url(client: @client)
36
+
37
+ i_should_see_translated_error_message("unauthorized_client")
38
+ end
39
+
40
+ scenario "allows the request when satisfies condition" do
41
+ @client.update(name: "admin")
42
+
43
+ visit authorization_endpoint_url(client: @client)
44
+ i_should_not_see_translated_error_message("unauthorized_client")
45
+ click_on "Authorize"
46
+
47
+ authorization_code = Doorkeeper::AccessGrant.first.token
48
+ create_access_token authorization_code, @client
49
+
50
+ access_token_should_exist_for(@client, @resource_owner)
51
+
52
+ should_not_have_json "error"
53
+
54
+ should_have_json "access_token", Doorkeeper::AccessToken.first.token
55
+ should_have_json "token_type", "Bearer"
56
+ should_have_json_within "expires_in", Doorkeeper::AccessToken.first.expires_in, 1
57
+ end
58
+ end
59
+
26
60
  context "with grant hashing enabled" do
27
61
  background do
28
62
  config_is_set(:token_secret_strategy, ::Doorkeeper::SecretStoring::Sha256Hash)
@@ -83,6 +117,17 @@ feature "Authorization Code Flow" do
83
117
  url_should_not_have_param("code_challenge_method")
84
118
  end
85
119
 
120
+ scenario "resource owner requests an access token without authorization code" do
121
+ create_access_token "", @client
122
+
123
+ access_token_should_not_exist
124
+
125
+ expect(Doorkeeper::AccessToken.count).to be_zero
126
+
127
+ should_have_json "error", "invalid_request"
128
+ should_have_json "error_description", translated_invalid_request_error_message(:missing_param, :code)
129
+ end
130
+
86
131
  scenario "resource owner requests an access token with authorization code" do
87
132
  visit authorization_endpoint_url(client: @client)
88
133
  click_on "Authorize"
@@ -110,6 +155,7 @@ feature "Authorization Code Flow" do
110
155
  expect(Doorkeeper::AccessToken.count).to be_zero
111
156
 
112
157
  should_have_json "error", "invalid_client"
158
+ should_have_json "error_description", translated_error_message(:invalid_client)
113
159
  end
114
160
 
115
161
  scenario "resource owner requests an access token with authorization code but without client id" do
@@ -123,6 +169,7 @@ feature "Authorization Code Flow" do
123
169
  expect(Doorkeeper::AccessToken.count).to be_zero
124
170
 
125
171
  should_have_json "error", "invalid_client"
172
+ should_have_json "error_description", translated_error_message(:invalid_client)
126
173
  end
127
174
 
128
175
  scenario "silently authorizes if matching token exists" do
@@ -165,6 +212,7 @@ feature "Authorization Code Flow" do
165
212
  create_access_token authorization_code, @client, code_verifier
166
213
 
167
214
  should_have_json "error", "invalid_grant"
215
+ should_have_json "error_description", translated_error_message(:invalid_grant)
168
216
  end
169
217
 
170
218
  scenario "mobile app requests an access token with authorization code and plain code challenge method" do
@@ -187,17 +235,32 @@ feature "Authorization Code Flow" do
187
235
  should_have_json_within "expires_in", Doorkeeper::AccessToken.first.expires_in, 1
188
236
  end
189
237
 
190
- scenario "mobile app requests an access token with authorization code and code_challenge" do
238
+ scenario "mobile app requests an access token with authorization code but without code_verifier" do
191
239
  visit authorization_endpoint_url(client: @client,
192
- code_challenge: code_verifier,
240
+ code_challenge: code_challenge,
193
241
  code_challenge_method: "plain")
194
242
  click_on "Authorize"
195
243
 
196
244
  authorization_code = current_params["code"]
197
- create_access_token authorization_code, @client, code_verifier: nil
245
+ create_access_token authorization_code, @client, nil
246
+
247
+ should_not_have_json "access_token"
248
+ should_have_json "error", "invalid_request"
249
+ should_have_json "error_description", translated_invalid_request_error_message(:missing_param, :code_verifier)
250
+ end
251
+
252
+ scenario "mobile app requests an access token with authorization code with wrong code_verifier" do
253
+ visit authorization_endpoint_url(client: @client,
254
+ code_challenge: code_challenge,
255
+ code_challenge_method: "plain")
256
+ click_on "Authorize"
257
+
258
+ authorization_code = current_params["code"]
259
+ create_access_token authorization_code, @client, "wrong_code_verifier"
198
260
 
199
261
  should_not_have_json "access_token"
200
262
  should_have_json "error", "invalid_grant"
263
+ should_have_json "error_description", translated_error_message(:invalid_grant)
201
264
  end
202
265
  end
203
266
 
@@ -238,19 +301,6 @@ feature "Authorization Code Flow" do
238
301
  should_have_json_within "expires_in", Doorkeeper::AccessToken.first.expires_in, 1
239
302
  end
240
303
 
241
- scenario "mobile app requests an access token with authorization code and without code_verifier" do
242
- visit authorization_endpoint_url(
243
- client: @client,
244
- code_challenge: code_challenge,
245
- code_challenge_method: "S256"
246
- )
247
- click_on "Authorize"
248
- authorization_code = current_params["code"]
249
- create_access_token authorization_code, @client
250
- should_have_json "error", "invalid_request"
251
- should_not_have_json "access_token"
252
- end
253
-
254
304
  scenario "mobile app requests an access token with authorization code and without secret" do
255
305
  visit authorization_endpoint_url(
256
306
  client: @client,
@@ -262,8 +312,9 @@ feature "Authorization Code Flow" do
262
312
  authorization_code = current_params["code"]
263
313
  page.driver.post token_endpoint_url(code: authorization_code, client_id: @client.uid,
264
314
  redirect_uri: @client.redirect_uri, code_verifier: code_verifier)
265
- should_have_json "error", "invalid_client"
266
315
  should_not_have_json "access_token"
316
+ should_have_json "error", "invalid_client"
317
+ should_have_json "error_description", translated_error_message(:invalid_client)
267
318
  end
268
319
 
269
320
  scenario "mobile app requests an access token with authorization code and without secret but is marked as not confidential" do
@@ -298,6 +349,7 @@ feature "Authorization Code Flow" do
298
349
 
299
350
  should_not_have_json "access_token"
300
351
  should_have_json "error", "invalid_request"
352
+ should_have_json "error_description", translated_invalid_request_error_message(:missing_param, :code_verifier)
301
353
  end
302
354
 
303
355
  scenario "mobile app requests an access token with authorization code with wrong verifier" do
@@ -313,6 +365,7 @@ feature "Authorization Code Flow" do
313
365
 
314
366
  should_not_have_json "access_token"
315
367
  should_have_json "error", "invalid_grant"
368
+ should_have_json "error_description", translated_error_message(:invalid_grant)
316
369
  end
317
370
 
318
371
  scenario "code_challenge_mehthod in token request is totally ignored" do
@@ -333,6 +386,7 @@ feature "Authorization Code Flow" do
333
386
 
334
387
  should_not_have_json "access_token"
335
388
  should_have_json "error", "invalid_grant"
389
+ should_have_json "error_description", translated_error_message(:invalid_grant)
336
390
  end
337
391
 
338
392
  scenario "expects to set code_challenge_method explicitely without fallback" do
@@ -344,16 +398,15 @@ feature "Authorization Code Flow" do
344
398
 
345
399
  context "when application scopes are present and no scope is passed" do
346
400
  background do
347
- @client.update(scopes: "public write read")
401
+ @client.update(scopes: "public write read default")
348
402
  end
349
403
 
350
- scenario "access grant has no scope" do
404
+ scenario "scope is invalid because default scope is different from application scope" do
351
405
  default_scopes_exist :admin
352
406
  visit authorization_endpoint_url(client: @client)
353
- click_on "Authorize"
354
- access_grant_should_exist_for(@client, @resource_owner)
355
- grant = Doorkeeper::AccessGrant.first
356
- expect(grant.scopes).to be_empty
407
+ response_status_should_be 200
408
+ i_should_not_see "Authorize"
409
+ i_should_see_translated_error_message :invalid_scope
357
410
  end
358
411
 
359
412
  scenario "access grant have scopes which are common in application scopees and default scopes" do
@@ -454,6 +507,7 @@ describe "Authorization Code Flow" do
454
507
 
455
508
  should_not_have_json "access_token"
456
509
  should_have_json "error", "invalid_grant"
510
+ should_have_json "error_description", translated_error_message(:invalid_grant)
457
511
  end
458
512
  end
459
513
  end
@@ -66,6 +66,44 @@ describe "Client Credentials Request" do
66
66
  end
67
67
  end
68
68
 
69
+ context "when configured to check application supported grant flow" do
70
+ before do
71
+ Doorkeeper.configuration.instance_variable_set(
72
+ :@allow_grant_flow_for_client,
73
+ ->(_grant_flow, client) { client.name == "admin" }
74
+ )
75
+ end
76
+
77
+ scenario "forbids the request when doesn't satisfy condition" do
78
+ client.update(name: "sample app")
79
+
80
+ headers = authorization client.uid, client.secret
81
+ params = { grant_type: "client_credentials" }
82
+
83
+ post "/oauth/token", params: params, headers: headers
84
+
85
+ should_have_json "error", "unauthorized_client"
86
+ should_have_json "error_description", translated_error_message(:unauthorized_client)
87
+ end
88
+
89
+ scenario "allows the request when satisfies condition" do
90
+ client.update(name: "admin")
91
+
92
+ headers = authorization client.uid, client.secret
93
+ params = { grant_type: "client_credentials" }
94
+
95
+ post "/oauth/token", params: params, headers: headers
96
+
97
+ should_have_json "access_token", Doorkeeper::AccessToken.first.token
98
+ should_have_json_within "expires_in", Doorkeeper.configuration.access_token_expires_in, 1
99
+ should_not_have_json "scope"
100
+ should_not_have_json "refresh_token"
101
+
102
+ should_not_have_json "error"
103
+ should_not_have_json "error_description"
104
+ end
105
+ end
106
+
69
107
  context "when application scopes contain some of the default scopes and no scope is passed" do
70
108
  before do
71
109
  client.update(scopes: "read write public")