doorkeeper 3.1.0 → 4.4.3

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 (195) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.github/ISSUE_TEMPLATE.md +25 -0
  4. data/.github/PULL_REQUEST_TEMPLATE.md +17 -0
  5. data/.gitignore +6 -1
  6. data/.hound.yml +2 -13
  7. data/.rubocop.yml +17 -0
  8. data/.travis.yml +26 -10
  9. data/Appraisals +18 -0
  10. data/CODE_OF_CONDUCT.md +46 -0
  11. data/CONTRIBUTING.md +2 -0
  12. data/Gemfile +5 -5
  13. data/NEWS.md +141 -2
  14. data/README.md +149 -66
  15. data/RELEASING.md +5 -12
  16. data/Rakefile +1 -1
  17. data/SECURITY.md +15 -0
  18. data/app/controllers/doorkeeper/application_controller.rb +4 -6
  19. data/app/controllers/doorkeeper/application_metal_controller.rb +3 -2
  20. data/app/controllers/doorkeeper/applications_controller.rb +18 -8
  21. data/app/controllers/doorkeeper/authorizations_controller.rb +1 -1
  22. data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -1
  23. data/app/controllers/doorkeeper/tokens_controller.rb +62 -15
  24. data/app/helpers/doorkeeper/dashboard_helper.rb +14 -10
  25. data/app/validators/redirect_uri_validator.rb +12 -2
  26. data/app/views/doorkeeper/applications/_delete_form.html.erb +1 -2
  27. data/app/views/doorkeeper/applications/_form.html.erb +13 -2
  28. data/app/views/doorkeeper/applications/index.html.erb +2 -0
  29. data/app/views/doorkeeper/applications/show.html.erb +4 -1
  30. data/app/views/doorkeeper/authorizations/new.html.erb +1 -1
  31. data/app/views/doorkeeper/authorized_applications/_delete_form.html.erb +1 -2
  32. data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
  33. data/app/views/layouts/doorkeeper/admin.html.erb +1 -1
  34. data/config/locales/en.yml +12 -7
  35. data/doorkeeper.gemspec +16 -11
  36. data/gemfiles/rails_4_2.gemfile +13 -0
  37. data/gemfiles/rails_5_0.gemfile +12 -0
  38. data/gemfiles/rails_5_1.gemfile +12 -0
  39. data/gemfiles/rails_5_2.gemfile +12 -0
  40. data/gemfiles/rails_master.gemfile +14 -0
  41. data/lib/doorkeeper/config.rb +119 -46
  42. data/lib/doorkeeper/engine.rb +11 -7
  43. data/lib/doorkeeper/errors.rb +18 -0
  44. data/lib/doorkeeper/grape/helpers.rb +14 -8
  45. data/lib/doorkeeper/helpers/controller.rb +8 -19
  46. data/lib/doorkeeper/models/access_grant_mixin.rb +10 -21
  47. data/lib/doorkeeper/models/access_token_mixin.rb +147 -43
  48. data/lib/doorkeeper/models/application_mixin.rb +33 -35
  49. data/lib/doorkeeper/models/concerns/accessible.rb +4 -0
  50. data/lib/doorkeeper/models/concerns/expirable.rb +15 -5
  51. data/lib/doorkeeper/models/concerns/orderable.rb +13 -0
  52. data/lib/doorkeeper/models/concerns/ownership.rb +6 -1
  53. data/lib/doorkeeper/models/concerns/revocable.rb +37 -2
  54. data/lib/doorkeeper/oauth/authorization/token.rb +22 -18
  55. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +20 -18
  56. data/lib/doorkeeper/oauth/authorization_code_request.rb +7 -5
  57. data/lib/doorkeeper/oauth/{request_concern.rb → base_request.rb} +9 -2
  58. data/lib/doorkeeper/oauth/base_response.rb +29 -0
  59. data/lib/doorkeeper/oauth/client/credentials.rb +21 -8
  60. data/lib/doorkeeper/oauth/client.rb +2 -3
  61. data/lib/doorkeeper/oauth/client_credentials/creator.rb +1 -1
  62. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +3 -2
  63. data/lib/doorkeeper/oauth/client_credentials/validation.rb +1 -1
  64. data/lib/doorkeeper/oauth/client_credentials_request.rb +8 -8
  65. data/lib/doorkeeper/oauth/code_response.rb +16 -16
  66. data/lib/doorkeeper/oauth/error.rb +2 -2
  67. data/lib/doorkeeper/oauth/error_response.rb +10 -10
  68. data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
  69. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +1 -1
  70. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +17 -1
  71. data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -4
  72. data/lib/doorkeeper/oauth/password_access_token_request.rb +8 -13
  73. data/lib/doorkeeper/oauth/pre_authorization.rb +5 -3
  74. data/lib/doorkeeper/oauth/refresh_token_request.rb +23 -14
  75. data/lib/doorkeeper/oauth/scopes.rb +18 -8
  76. data/lib/doorkeeper/oauth/token.rb +20 -21
  77. data/lib/doorkeeper/oauth/token_introspection.rb +128 -0
  78. data/lib/doorkeeper/oauth/token_request.rb +1 -2
  79. data/lib/doorkeeper/oauth/token_response.rb +1 -1
  80. data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -0
  81. data/lib/doorkeeper/orm/active_record/access_token.rb +34 -8
  82. data/lib/doorkeeper/orm/active_record/application.rb +48 -11
  83. data/lib/doorkeeper/orm/active_record.rb +17 -22
  84. data/lib/doorkeeper/rails/helpers.rb +6 -9
  85. data/lib/doorkeeper/rails/routes/mapper.rb +4 -4
  86. data/lib/doorkeeper/rails/routes/mapping.rb +1 -1
  87. data/lib/doorkeeper/rails/routes.rb +17 -11
  88. data/lib/doorkeeper/request/authorization_code.rb +7 -1
  89. data/lib/doorkeeper/request/password.rb +2 -2
  90. data/lib/doorkeeper/request/refresh_token.rb +1 -1
  91. data/lib/doorkeeper/request.rb +7 -1
  92. data/lib/doorkeeper/server.rb +0 -8
  93. data/lib/doorkeeper/validations.rb +3 -2
  94. data/lib/doorkeeper/version.rb +34 -1
  95. data/lib/doorkeeper.rb +10 -2
  96. data/lib/generators/doorkeeper/add_client_confidentiality_generator.rb +31 -0
  97. data/lib/generators/doorkeeper/application_owner_generator.rb +11 -2
  98. data/lib/generators/doorkeeper/migration_generator.rb +13 -1
  99. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +35 -0
  100. data/lib/generators/doorkeeper/templates/add_confidential_to_application_migration.rb.erb +11 -0
  101. data/{spec/dummy/db/migrate/20130902175349_add_owner_to_application.rb → lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb} +1 -1
  102. data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb +11 -0
  103. data/lib/generators/doorkeeper/templates/initializer.rb +38 -6
  104. data/lib/generators/doorkeeper/templates/migration.rb.erb +69 -0
  105. data/spec/controllers/application_metal_controller.rb +10 -0
  106. data/spec/controllers/applications_controller_spec.rb +15 -4
  107. data/spec/controllers/authorizations_controller_spec.rb +74 -27
  108. data/spec/controllers/protected_resources_controller_spec.rb +70 -32
  109. data/spec/controllers/token_info_controller_spec.rb +17 -13
  110. data/spec/controllers/tokens_controller_spec.rb +198 -12
  111. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -4
  112. data/spec/dummy/app/controllers/home_controller.rb +1 -1
  113. data/spec/dummy/app/controllers/metal_controller.rb +1 -1
  114. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +3 -3
  115. data/spec/dummy/app/models/user.rb +0 -4
  116. data/spec/dummy/config/application.rb +2 -36
  117. data/spec/dummy/config/environment.rb +1 -1
  118. data/spec/dummy/config/environments/test.rb +4 -15
  119. data/spec/dummy/config/initializers/doorkeeper.rb +19 -3
  120. data/spec/dummy/config/initializers/new_framework_defaults.rb +6 -0
  121. data/spec/dummy/config/initializers/secret_token.rb +0 -1
  122. data/spec/dummy/db/migrate/20111122132257_create_users.rb +3 -1
  123. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +3 -1
  124. data/{lib/generators/doorkeeper/templates/migration.rb → spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb} +16 -4
  125. data/{lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb → spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb} +4 -2
  126. data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +13 -0
  127. data/spec/dummy/db/migrate/20180210183654_add_confidential_to_application.rb +13 -0
  128. data/spec/dummy/db/schema.rb +24 -22
  129. data/spec/factories.rb +4 -2
  130. data/spec/generators/application_owner_generator_spec.rb +24 -5
  131. data/spec/generators/migration_generator_spec.rb +24 -3
  132. data/spec/generators/previous_refresh_token_generator_spec.rb +57 -0
  133. data/spec/grape/grape_integration_spec.rb +135 -0
  134. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
  135. data/spec/lib/config_spec.rb +159 -14
  136. data/spec/lib/doorkeeper_spec.rb +135 -13
  137. data/spec/lib/models/expirable_spec.rb +0 -1
  138. data/spec/lib/models/revocable_spec.rb +27 -4
  139. data/spec/lib/oauth/authorization/uri_builder_spec.rb +1 -2
  140. data/spec/lib/oauth/authorization_code_request_spec.rb +55 -12
  141. data/spec/lib/oauth/base_request_spec.rb +155 -0
  142. data/spec/lib/oauth/base_response_spec.rb +45 -0
  143. data/spec/lib/oauth/client/credentials_spec.rb +45 -2
  144. data/spec/lib/oauth/client_credentials/creator_spec.rb +1 -1
  145. data/spec/lib/oauth/client_credentials_integration_spec.rb +1 -1
  146. data/spec/lib/oauth/client_credentials_request_spec.rb +1 -0
  147. data/spec/lib/oauth/code_request_spec.rb +1 -3
  148. data/spec/lib/oauth/code_response_spec.rb +34 -0
  149. data/spec/lib/oauth/error_response_spec.rb +9 -9
  150. data/spec/lib/oauth/error_spec.rb +1 -1
  151. data/spec/lib/oauth/helpers/uri_checker_spec.rb +115 -1
  152. data/spec/lib/oauth/invalid_token_response_spec.rb +36 -8
  153. data/spec/lib/oauth/password_access_token_request_spec.rb +14 -8
  154. data/spec/lib/oauth/pre_authorization_spec.rb +12 -7
  155. data/spec/lib/oauth/refresh_token_request_spec.rb +52 -9
  156. data/spec/lib/oauth/scopes_spec.rb +28 -2
  157. data/spec/lib/oauth/token_request_spec.rb +6 -8
  158. data/spec/lib/oauth/token_spec.rb +12 -5
  159. data/spec/lib/server_spec.rb +10 -3
  160. data/spec/models/doorkeeper/access_grant_spec.rb +1 -1
  161. data/spec/models/doorkeeper/access_token_spec.rb +116 -48
  162. data/spec/models/doorkeeper/application_spec.rb +145 -29
  163. data/spec/requests/applications/applications_request_spec.rb +5 -5
  164. data/spec/requests/endpoints/authorization_spec.rb +5 -6
  165. data/spec/requests/endpoints/token_spec.rb +8 -1
  166. data/spec/requests/flows/authorization_code_errors_spec.rb +11 -1
  167. data/spec/requests/flows/authorization_code_spec.rb +6 -13
  168. data/spec/requests/flows/client_credentials_spec.rb +29 -1
  169. data/spec/requests/flows/implicit_grant_errors_spec.rb +2 -2
  170. data/spec/requests/flows/password_spec.rb +118 -15
  171. data/spec/requests/flows/refresh_token_spec.rb +89 -19
  172. data/spec/requests/flows/revoke_token_spec.rb +105 -91
  173. data/spec/requests/protected_resources/metal_spec.rb +1 -1
  174. data/spec/requests/protected_resources/private_api_spec.rb +1 -1
  175. data/spec/routing/custom_controller_routes_spec.rb +4 -0
  176. data/spec/routing/default_routes_spec.rb +5 -1
  177. data/spec/spec_helper.rb +2 -0
  178. data/spec/spec_helper_integration.rb +22 -4
  179. data/spec/support/dependencies/factory_girl.rb +2 -2
  180. data/spec/support/helpers/access_token_request_helper.rb +1 -1
  181. data/spec/support/helpers/model_helper.rb +34 -7
  182. data/spec/support/helpers/request_spec_helper.rb +17 -5
  183. data/spec/support/helpers/url_helper.rb +9 -8
  184. data/spec/support/http_method_shim.rb +38 -0
  185. data/spec/support/shared/controllers_shared_context.rb +15 -10
  186. data/spec/support/shared/models_shared_examples.rb +5 -5
  187. data/spec/validators/redirect_uri_validator_spec.rb +51 -6
  188. data/spec/version/version_spec.rb +15 -0
  189. metadata +128 -46
  190. data/lib/doorkeeper/oauth/client/methods.rb +0 -18
  191. data/lib/generators/doorkeeper/application_scopes_generator.rb +0 -34
  192. data/lib/generators/doorkeeper/templates/add_scopes_to_oauth_applications.rb +0 -5
  193. data/spec/dummy/db/migrate/20130902165751_create_doorkeeper_tables.rb +0 -41
  194. data/spec/dummy/db/migrate/20141209001746_add_scopes_to_oauth_applications.rb +0 -5
  195. data/spec/lib/oauth/client/methods_spec.rb +0 -54
@@ -43,19 +43,19 @@ module Doorkeeper::OAuth
43
43
  end
44
44
  end
45
45
 
46
- describe '.authenticate_info' do
46
+ describe '.headers' do
47
47
  let(:error_response) { ErrorResponse.new(name: :some_error, state: :some_state) }
48
- subject { error_response.authenticate_info }
48
+ subject { error_response.headers }
49
49
 
50
- it { expect(subject).to include("realm=\"#{error_response.realm}\"") }
51
- it { expect(subject).to include("error=\"#{error_response.name}\"") }
52
- it { expect(subject).to include("error_description=\"#{error_response.description}\"") }
53
- end
50
+ it { expect(subject).to include 'WWW-Authenticate' }
54
51
 
55
- describe '.headers' do
56
- subject { ErrorResponse.new(name: :some_error, state: :some_state).headers }
52
+ describe "WWW-Authenticate header" do
53
+ subject { error_response.headers["WWW-Authenticate"] }
57
54
 
58
- it { expect(subject).to include 'WWW-Authenticate' }
55
+ it { expect(subject).to include("realm=\"#{error_response.realm}\"") }
56
+ it { expect(subject).to include("error=\"#{error_response.name}\"") }
57
+ it { expect(subject).to include("error_description=\"#{error_response.description}\"") }
58
+ end
59
59
  end
60
60
  end
61
61
  end
@@ -13,7 +13,7 @@ module Doorkeeper::OAuth
13
13
  it 'is translated from translation messages' do
14
14
  expect(I18n).to receive(:translate).with(
15
15
  :some_error,
16
- scope: [:doorkeeper, :errors, :messages],
16
+ scope: %i[doorkeeper errors messages],
17
17
  default: :server_error
18
18
  )
19
19
  error.description
@@ -5,6 +5,11 @@ require 'doorkeeper/oauth/helpers/uri_checker'
5
5
  module Doorkeeper::OAuth::Helpers
6
6
  describe URIChecker do
7
7
  describe '.valid?' do
8
+ it 'is valid for native uris' do
9
+ uri = 'urn:ietf:wg:oauth:2.0:oob'
10
+ expect(URIChecker.valid?(uri)).to be_truthy
11
+ end
12
+
8
13
  it 'is valid for valid uris' do
9
14
  uri = 'http://app.co'
10
15
  expect(URIChecker.valid?(uri)).to be_truthy
@@ -39,6 +44,11 @@ module Doorkeeper::OAuth::Helpers
39
44
  uri = 'http://'
40
45
  expect(URIChecker.valid?(uri)).to be_falsey
41
46
  end
47
+
48
+ it 'is invalid if is not an uri' do
49
+ uri = ' '
50
+ expect(URIChecker.valid?(uri)).to be_falsey
51
+ end
42
52
  end
43
53
 
44
54
  describe '.matches?' do
@@ -64,6 +74,44 @@ module Doorkeeper::OAuth::Helpers
64
74
  client_uri = 'http://example.com?app.co=test'
65
75
  expect(URIChecker.matches?(uri, client_uri)).to be_falsey
66
76
  end
77
+
78
+ context "client registered query params" do
79
+ it "doesn't allow query being absent" do
80
+ uri = 'http://app.co'
81
+ client_uri = 'http://app.co/?vendorId=AJ4L7XXW9'
82
+ expect(URIChecker.matches?(uri, client_uri)).to be_falsey
83
+ end
84
+
85
+ it "is false if query values differ but key same" do
86
+ uri = 'http://app.co/?vendorId=pancakes'
87
+ client_uri = 'http://app.co/?vendorId=waffles'
88
+ expect(URIChecker.matches?(uri, client_uri)).to be_falsey
89
+ end
90
+
91
+ it "is false if query values same but key differs" do
92
+ uri = 'http://app.co/?foo=pancakes'
93
+ client_uri = 'http://app.co/?bar=pancakes'
94
+ expect(URIChecker.matches?(uri, client_uri)).to be_falsey
95
+ end
96
+
97
+ it "is false if query present and match, but unknown queries present" do
98
+ uri = 'http://app.co/?vendorId=pancakes&unknown=query'
99
+ client_uri = 'http://app.co/?vendorId=waffles'
100
+ expect(URIChecker.matches?(uri, client_uri)).to be_falsey
101
+ end
102
+
103
+ it "is true if queries are present and matche" do
104
+ uri = 'http://app.co/?vendorId=AJ4L7XXW9&foo=bar'
105
+ client_uri = 'http://app.co/?vendorId=AJ4L7XXW9&foo=bar'
106
+ expect(URIChecker.matches?(uri, client_uri)).to be_truthy
107
+ end
108
+
109
+ it "is true if queries are present, match and in different order" do
110
+ uri = 'http://app.co/?bing=bang&foo=bar'
111
+ client_uri = 'http://app.co/?foo=bar&bing=bang'
112
+ expect(URIChecker.matches?(uri, client_uri)).to be_truthy
113
+ end
114
+ end
67
115
  end
68
116
 
69
117
  describe '.valid_for_authorization?' do
@@ -96,9 +144,75 @@ module Doorkeeper::OAuth::Helpers
96
144
  end
97
145
 
98
146
  it 'is false if invalid' do
99
- uri = client_uri = 'http://app.co/aaa?waffles=abc'
147
+ uri = 'http://app.co/aaa?pankcakes=abc'
148
+ client_uri = 'http://app.co/aaa?waffles=abc'
100
149
  expect(URIChecker.valid_for_authorization?(uri, client_uri)).to be false
101
150
  end
151
+
152
+ it 'calls .matches?' do
153
+ uri = 'http://app.co/aaa?pankcakes=abc'
154
+ client_uri = 'http://app.co/aaa?waffles=abc'
155
+ expect(URIChecker).to receive(:matches?).with(uri, client_uri).once
156
+ URIChecker.valid_for_authorization?(uri, client_uri)
157
+ end
158
+
159
+ it 'calls .valid?' do
160
+ uri = 'http://app.co/aaa?pankcakes=abc'
161
+ client_uri = 'http://app.co/aaa?waffles=abc'
162
+ expect(URIChecker).to receive(:valid?).with(uri).once
163
+ URIChecker.valid_for_authorization?(uri, client_uri)
164
+ end
165
+ end
166
+
167
+ describe '.query_matches?' do
168
+ it 'is true if no queries' do
169
+ expect(URIChecker.query_matches?('', '')).to be_truthy
170
+ expect(URIChecker.query_matches?(nil, nil)).to be_truthy
171
+ end
172
+
173
+ it 'is true if same query' do
174
+ expect(URIChecker.query_matches?('foo', 'foo')).to be_truthy
175
+ end
176
+
177
+ it 'is false if different query' do
178
+ expect(URIChecker.query_matches?('foo', 'bar')).to be_falsey
179
+ end
180
+
181
+ it 'is true if same queries' do
182
+ expect(URIChecker.query_matches?('foo&bar', 'foo&bar')).to be_truthy
183
+ end
184
+
185
+ it 'is true if same queries, different order' do
186
+ expect(URIChecker.query_matches?('foo&bar', 'bar&foo')).to be_truthy
187
+ end
188
+
189
+ it 'is false if one different query' do
190
+ expect(URIChecker.query_matches?('foo&bang', 'foo&bing')).to be_falsey
191
+ end
192
+
193
+ it 'is true if same query with same value' do
194
+ expect(URIChecker.query_matches?('foo=bar', 'foo=bar')).to be_truthy
195
+ end
196
+
197
+ it 'is true if same queries with same values' do
198
+ expect(URIChecker.query_matches?('foo=bar&bing=bang', 'foo=bar&bing=bang')).to be_truthy
199
+ end
200
+
201
+ it 'is true if same queries with same values, different order' do
202
+ expect(URIChecker.query_matches?('foo=bar&bing=bang', 'bing=bang&foo=bar')).to be_truthy
203
+ end
204
+
205
+ it 'is false if same query with different value' do
206
+ expect(URIChecker.query_matches?('foo=bar', 'foo=bang')).to be_falsey
207
+ end
208
+
209
+ it 'is false if some queries missing' do
210
+ expect(URIChecker.query_matches?('foo=bar', 'foo=bar&bing=bang')).to be_falsey
211
+ end
212
+
213
+ it 'is false if some queries different value' do
214
+ expect(URIChecker.query_matches?('foo=bar&bing=bang', 'foo=bar&bing=banana')).to be_falsey
215
+ end
102
216
  end
103
217
  end
104
218
  end
@@ -5,23 +5,51 @@ require 'doorkeeper/oauth/invalid_token_response'
5
5
 
6
6
  module Doorkeeper::OAuth
7
7
  describe InvalidTokenResponse do
8
- describe '#name' do
8
+ describe "#name" do
9
9
  it { expect(subject.name).to eq(:invalid_token) }
10
10
  end
11
11
 
12
- describe '#status' do
12
+ describe "#status" do
13
13
  it { expect(subject.status).to eq(:unauthorized) }
14
14
  end
15
15
 
16
16
  describe :from_access_token do
17
- it 'revoked' do
18
- response = InvalidTokenResponse.from_access_token double(revoked?: true, expired?: true)
19
- expect(response.description).to include('revoked')
17
+ let(:response) { InvalidTokenResponse.from_access_token(access_token) }
18
+
19
+ context "revoked" do
20
+ let(:access_token) { double(revoked?: true, expired?: true) }
21
+
22
+ it "sets a description" do
23
+ expect(response.description).to include("revoked")
24
+ end
25
+
26
+ it "sets the reason" do
27
+ expect(response.reason).to eq(:revoked)
28
+ end
20
29
  end
21
30
 
22
- it 'expired' do
23
- response = InvalidTokenResponse.from_access_token double(revoked?: false, expired?: true)
24
- expect(response.description).to include('expired')
31
+ context "expired" do
32
+ let(:access_token) { double(revoked?: false, expired?: true) }
33
+
34
+ it "sets a description" do
35
+ expect(response.description).to include("expired")
36
+ end
37
+
38
+ it "sets the reason" do
39
+ expect(response.reason).to eq(:expired)
40
+ end
41
+ end
42
+
43
+ context "unknown" do
44
+ let(:access_token) { double(revoked?: false, expired?: false) }
45
+
46
+ it "sets a description" do
47
+ expect(response.description).to include("invalid")
48
+ end
49
+
50
+ it "sets the reason" do
51
+ expect(response.reason).to eq(:unknown)
52
+ end
25
53
  end
26
54
  end
27
55
  end
@@ -11,23 +11,22 @@ module Doorkeeper::OAuth
11
11
  custom_access_token_expires_in: ->(_app) { nil }
12
12
  )
13
13
  end
14
- let(:credentials) { Client::Credentials.new(client.uid, client.secret) }
15
- let(:client) { FactoryGirl.create(:application) }
14
+ let(:client) { FactoryBot.create(:application) }
16
15
  let(:owner) { double :owner, id: 99 }
17
16
 
18
17
  subject do
19
- PasswordAccessTokenRequest.new(server, credentials, owner)
18
+ PasswordAccessTokenRequest.new(server, client, owner)
20
19
  end
21
20
 
22
21
  it 'issues a new token for the client' do
23
22
  expect do
24
23
  subject.authorize
25
- end.to change { client.access_tokens.count }.by(1)
24
+ end.to change { client.reload.access_tokens.count }.by(1)
26
25
  end
27
26
 
28
27
  it 'issues a new token without a client' do
29
28
  expect do
30
- subject.credentials = nil
29
+ subject.client = nil
31
30
  subject.authorize
32
31
  end.to change { Doorkeeper::AccessToken.count }.by(1)
33
32
  end
@@ -35,6 +34,7 @@ module Doorkeeper::OAuth
35
34
  it 'does not issue a new token with an invalid client' do
36
35
  expect do
37
36
  subject.client = nil
37
+ subject.parameters = { client_id: 'bad_id' }
38
38
  subject.authorize
39
39
  end.to_not change { Doorkeeper::AccessToken.count }
40
40
 
@@ -48,12 +48,12 @@ module Doorkeeper::OAuth
48
48
  end
49
49
 
50
50
  it 'optionally accepts the client' do
51
- subject.credentials = nil
51
+ subject.client = nil
52
52
  expect(subject).to be_valid
53
53
  end
54
54
 
55
55
  it 'creates token even when there is already one (default)' do
56
- FactoryGirl.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
56
+ FactoryBot.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
57
57
  expect do
58
58
  subject.authorize
59
59
  end.to change { Doorkeeper::AccessToken.count }.by(1)
@@ -61,12 +61,18 @@ module Doorkeeper::OAuth
61
61
 
62
62
  it 'skips token creation if there is already one' do
63
63
  allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
64
- FactoryGirl.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
64
+ FactoryBot.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
65
65
  expect do
66
66
  subject.authorize
67
67
  end.to_not change { Doorkeeper::AccessToken.count }
68
68
  end
69
69
 
70
+ it "calls configured request callback methods" do
71
+ expect(Doorkeeper.configuration.before_successful_strategy_response).to receive(:call).with(subject).once
72
+ expect(Doorkeeper.configuration.after_successful_strategy_response).to receive(:call).with(subject, instance_of(Doorkeeper::OAuth::TokenResponse)).once
73
+ subject.authorize
74
+ end
75
+
70
76
  describe 'with scopes' do
71
77
  subject do
72
78
  PasswordAccessTokenRequest.new(server, client, owner, scope: 'public')
@@ -123,14 +123,19 @@ module Doorkeeper::OAuth
123
123
  expect(subject.scopes).to eq(Scopes.from_string('default'))
124
124
  end
125
125
 
126
- it 'accepts test uri' do
127
- subject.redirect_uri = 'urn:ietf:wg:oauth:2.0:oob'
128
- expect(subject).to be_authorizable
129
- end
126
+ context 'with native redirect uri' do
127
+ let(:native_redirect_uri) { 'urn:ietf:wg:oauth:2.0:oob' }
130
128
 
131
- it 'matches the redirect uri against client\'s one' do
132
- subject.redirect_uri = 'http://nothesame.com'
133
- expect(subject).not_to be_authorizable
129
+ it 'accepts redirect_uri when it matches with the client' do
130
+ subject.redirect_uri = native_redirect_uri
131
+ allow(subject.client).to receive(:redirect_uri) { native_redirect_uri }
132
+ expect(subject).to be_authorizable
133
+ end
134
+
135
+ it 'invalidates redirect_uri when it does\'n match with the client' do
136
+ subject.redirect_uri = native_redirect_uri
137
+ expect(subject).not_to be_authorizable
138
+ end
134
139
  end
135
140
 
136
141
  it 'stores the state' do
@@ -2,24 +2,29 @@ require 'spec_helper_integration'
2
2
 
3
3
  module Doorkeeper::OAuth
4
4
  describe RefreshTokenRequest do
5
+ before do
6
+ allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
7
+ end
8
+
5
9
  let(:server) do
6
10
  double :server,
7
11
  access_token_expires_in: 2.minutes,
8
12
  custom_access_token_expires_in: -> (_oauth_client) { nil }
9
13
  end
14
+
10
15
  let(:refresh_token) do
11
- FactoryGirl.create(:access_token, use_refresh_token: true)
16
+ FactoryBot.create(:access_token, use_refresh_token: true)
12
17
  end
18
+
13
19
  let(:client) { refresh_token.application }
14
20
  let(:credentials) { Client::Credentials.new(client.uid, client.secret) }
15
21
 
16
22
  subject { RefreshTokenRequest.new server, refresh_token, credentials }
17
23
 
18
24
  it 'issues a new token for the client' do
19
- expect do
20
- subject.authorize
21
- end.to change { client.access_tokens.count }.by(1)
22
- expect(client.reload.access_tokens.last.expires_in).to eq(120)
25
+ expect { subject.authorize }.to change { client.reload.access_tokens.count }.by(1)
26
+ # #sort_by used for MongoDB ORM extensions for valid ordering
27
+ expect(client.reload.access_tokens.sort_by(&:created_at).last.expires_in).to eq(120)
23
28
  end
24
29
 
25
30
  it 'issues a new token for the client with custom expires_in' do
@@ -27,15 +32,24 @@ module Doorkeeper::OAuth
27
32
  access_token_expires_in: 2.minutes,
28
33
  custom_access_token_expires_in: ->(_oauth_client) { 1234 }
29
34
 
35
+ allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
36
+
30
37
  RefreshTokenRequest.new(server, refresh_token, credentials).authorize
31
38
 
32
- expect(client.reload.access_tokens.last.expires_in).to eq(1234)
39
+ # #sort_by used for MongoDB ORM extensions for valid ordering
40
+ expect(client.reload.access_tokens.sort_by(&:created_at).last.expires_in).to eq(1234)
33
41
  end
34
42
 
35
43
  it 'revokes the previous token' do
36
44
  expect { subject.authorize }.to change { refresh_token.revoked? }.from(false).to(true)
37
45
  end
38
46
 
47
+ it "calls configured request callback methods" do
48
+ expect(Doorkeeper.configuration.before_successful_strategy_response).to receive(:call).with(subject).once
49
+ expect(Doorkeeper.configuration.after_successful_strategy_response).to receive(:call).with(subject, instance_of(Doorkeeper::OAuth::TokenResponse)).once
50
+ subject.authorize
51
+ end
52
+
39
53
  it 'requires the refresh token' do
40
54
  subject.refresh_token = nil
41
55
  subject.validate
@@ -49,7 +63,7 @@ module Doorkeeper::OAuth
49
63
  end
50
64
 
51
65
  it "requires the token's client and current client to match" do
52
- subject.client = FactoryGirl.create(:application)
66
+ subject.client = FactoryBot.create(:application)
53
67
  subject.validate
54
68
  expect(subject.error).to eq(:invalid_grant)
55
69
  end
@@ -67,8 +81,37 @@ module Doorkeeper::OAuth
67
81
  expect(subject).to be_valid
68
82
  end
69
83
 
84
+ context 'refresh tokens expire on access token use' do
85
+ let(:server) do
86
+ double :server,
87
+ access_token_expires_in: 2.minutes,
88
+ custom_access_token_expires_in: ->(_oauth_client) { 1234 }
89
+ end
90
+
91
+ before do
92
+ allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(true)
93
+ end
94
+
95
+ it 'issues a new token for the client' do
96
+ expect { subject.authorize }.to change { client.reload.access_tokens.count }.by(1)
97
+ end
98
+
99
+ it 'does not revoke the previous token' do
100
+ subject.authorize
101
+ expect(refresh_token).not_to be_revoked
102
+ end
103
+
104
+ it 'sets the previous refresh token in the new access token' do
105
+ subject.authorize
106
+ expect(
107
+ # #sort_by used for MongoDB ORM extensions for valid ordering
108
+ client.access_tokens.sort_by(&:created_at).last.previous_refresh_token
109
+ ).to eq(refresh_token.refresh_token)
110
+ end
111
+ end
112
+
70
113
  context 'clientless access tokens' do
71
- let!(:refresh_token) { FactoryGirl.create(:clientless_access_token, use_refresh_token: true) }
114
+ let!(:refresh_token) { FactoryBot.create(:clientless_access_token, use_refresh_token: true) }
72
115
 
73
116
  subject { RefreshTokenRequest.new server, refresh_token, nil }
74
117
 
@@ -79,7 +122,7 @@ module Doorkeeper::OAuth
79
122
 
80
123
  context 'with scopes' do
81
124
  let(:refresh_token) do
82
- FactoryGirl.create :access_token,
125
+ FactoryBot.create :access_token,
83
126
  use_refresh_token: true,
84
127
  scopes: 'public write'
85
128
  end
@@ -62,15 +62,19 @@ module Doorkeeper::OAuth
62
62
  describe '#+' do
63
63
  it 'can add to another scope object' do
64
64
  scopes = Scopes.from_string('public') + Scopes.from_string('admin')
65
- expect(scopes.all).to eq(%w(public admin))
65
+ expect(scopes.all).to eq(%w[public admin])
66
66
  end
67
67
 
68
68
  it 'does not change the existing object' do
69
69
  origin = Scopes.from_string('public')
70
- new_scope = origin + Scopes.from_string('admin')
71
70
  expect(origin.to_s).to eq('public')
72
71
  end
73
72
 
73
+ it 'can add an array to a scope object' do
74
+ scopes = Scopes.from_string('public') + ['admin']
75
+ expect(scopes.all).to eq(%w[public admin])
76
+ end
77
+
74
78
  it 'raises an error if cannot handle addition' do
75
79
  expect do
76
80
  Scopes.from_string('public') + 'admin'
@@ -78,6 +82,24 @@ module Doorkeeper::OAuth
78
82
  end
79
83
  end
80
84
 
85
+ describe '#&' do
86
+ it 'can get intersection with another scope object' do
87
+ scopes = Scopes.from_string('public admin') & Scopes.from_string('write admin')
88
+ expect(scopes.all).to eq(%w[admin])
89
+ end
90
+
91
+ it 'does not change the existing object' do
92
+ origin = Scopes.from_string('public admin')
93
+ origin & Scopes.from_string('write admin')
94
+ expect(origin.to_s).to eq('public admin')
95
+ end
96
+
97
+ it 'can get intersection with an array' do
98
+ scopes = Scopes.from_string('public admin') & %w[write admin]
99
+ expect(scopes.all).to eq(%w[admin])
100
+ end
101
+ end
102
+
81
103
  describe '#==' do
82
104
  it 'is equal to another set of scopes' do
83
105
  expect(Scopes.from_string('public')).to eq(Scopes.from_string('public'))
@@ -90,6 +112,10 @@ module Doorkeeper::OAuth
90
112
  it 'differs from another set of scopes when scopes are not the same' do
91
113
  expect(Scopes.from_string('public write')).not_to eq(Scopes.from_string('write'))
92
114
  end
115
+
116
+ it "does not raise an error when compared to a non-enumerable object" do
117
+ expect { Scopes.from_string("public") == false }.not_to raise_error
118
+ end
93
119
  end
94
120
 
95
121
  describe '#has_scopes?' do
@@ -6,6 +6,7 @@ module Doorkeeper::OAuth
6
6
  scopes = double(all: ['public'])
7
7
  double(:application, id: 9990, scopes: scopes)
8
8
  end
9
+
9
10
  let :pre_auth do
10
11
  double(
11
12
  :pre_auth,
@@ -38,9 +39,7 @@ module Doorkeeper::OAuth
38
39
 
39
40
  it 'does not create token when not authorizable' do
40
41
  allow(pre_auth).to receive(:authorizable?).and_return(false)
41
- expect do
42
- subject.authorize
43
- end.to_not change { Doorkeeper::AccessToken.count }
42
+ expect { subject.authorize }.not_to change { Doorkeeper::AccessToken.count }
44
43
  end
45
44
 
46
45
  it 'returns a error response' do
@@ -75,7 +74,7 @@ module Doorkeeper::OAuth
75
74
 
76
75
  it 'creates a new token if scopes do not match' do
77
76
  allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
78
- FactoryGirl.create(:access_token, application_id: pre_auth.client.id,
77
+ FactoryBot.create(:access_token, application_id: pre_auth.client.id,
79
78
  resource_owner_id: owner.id, scopes: '')
80
79
  expect do
81
80
  subject.authorize
@@ -86,12 +85,11 @@ module Doorkeeper::OAuth
86
85
  allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
87
86
  allow(application.scopes).to receive(:has_scopes?).and_return(true)
88
87
  allow(application.scopes).to receive(:all?).and_return(true)
89
- FactoryGirl.create(:access_token, application_id: pre_auth.client.id,
88
+
89
+ FactoryBot.create(:access_token, application_id: pre_auth.client.id,
90
90
  resource_owner_id: owner.id, scopes: 'public')
91
91
 
92
- expect do
93
- subject.authorize
94
- end.to_not change { Doorkeeper::AccessToken.count }
92
+ expect { subject.authorize }.not_to change { Doorkeeper::AccessToken.count }
95
93
  end
96
94
  end
97
95
  end
@@ -30,7 +30,7 @@ module Doorkeeper
30
30
  it 'stops at the first credentials found' do
31
31
  not_called_method = double
32
32
  expect(not_called_method).not_to receive(:call)
33
- Token.from_request request, ->(r) {}, method, not_called_method
33
+ Token.from_request request, ->(_r) {}, method, not_called_method
34
34
  end
35
35
 
36
36
  it 'returns the credential from extractor method' do
@@ -96,13 +96,20 @@ module Doorkeeper
96
96
  end
97
97
 
98
98
  describe :authenticate do
99
- let(:finder) { double :finder }
100
-
101
- it 'calls the finder if token was found' do
102
- token = ->(r) { 'token' }
99
+ it 'calls the finder if token was returned' do
100
+ token = ->(_r) { 'token' }
103
101
  expect(AccessToken).to receive(:by_token).with('token')
104
102
  Token.authenticate double, token
105
103
  end
104
+
105
+ it 'revokes previous refresh_token if token was found' do
106
+ token = ->(_r) { 'token' }
107
+ expect(
108
+ AccessToken
109
+ ).to receive(:by_token).with('token').and_return(token)
110
+ expect(token).to receive(:revoke_previous_refresh_token!)
111
+ Token.authenticate double, token
112
+ end
106
113
  end
107
114
  end
108
115
  end
@@ -1,7 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'active_support/all'
3
- require 'doorkeeper/errors'
4
- require 'doorkeeper/server'
5
2
 
6
3
  describe Doorkeeper::Server do
7
4
  let(:fake_class) { double :fake_class }
@@ -48,5 +45,15 @@ describe Doorkeeper::Server do
48
45
  expect(fake_class).to receive(:new).with(subject)
49
46
  subject.authorization_request :code
50
47
  end
48
+
49
+ it 'builds the request with composit strategy name' do
50
+ allow(Doorkeeper.configuration).
51
+ to receive(:authorization_response_types).
52
+ and_return(['id_token token'])
53
+
54
+ stub_const 'Doorkeeper::Request::IdTokenToken', fake_class
55
+ expect(fake_class).to receive(:new).with(subject)
56
+ subject.authorization_request 'id_token token'
57
+ end
51
58
  end
52
59
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper_integration'
2
2
 
3
3
  describe Doorkeeper::AccessGrant do
4
- subject { FactoryGirl.build(:access_grant) }
4
+ subject { FactoryBot.build(:access_grant) }
5
5
 
6
6
  it { expect(subject).to be_valid }
7
7