doorkeeper 5.0.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. checksums.yaml +5 -5
  2. data/.hound.yml +2 -1
  3. data/.rubocop.yml +37 -4
  4. data/.travis.yml +16 -21
  5. data/Appraisals +29 -7
  6. data/Dangerfile +67 -0
  7. data/Gemfile +19 -5
  8. data/NEWS.md +107 -10
  9. data/README.md +75 -470
  10. data/Rakefile +10 -8
  11. data/app/controllers/doorkeeper/application_controller.rb +3 -2
  12. data/app/controllers/doorkeeper/application_metal_controller.rb +4 -13
  13. data/app/controllers/doorkeeper/applications_controller.rb +21 -7
  14. data/app/controllers/doorkeeper/authorizations_controller.rb +3 -3
  15. data/app/controllers/doorkeeper/authorized_applications_controller.rb +2 -0
  16. data/app/controllers/doorkeeper/token_info_controller.rb +4 -2
  17. data/app/controllers/doorkeeper/tokens_controller.rb +13 -11
  18. data/app/helpers/doorkeeper/dashboard_helper.rb +3 -1
  19. data/app/validators/redirect_uri_validator.rb +7 -2
  20. data/app/views/doorkeeper/applications/_form.html.erb +6 -0
  21. data/app/views/doorkeeper/applications/show.html.erb +1 -1
  22. data/app/views/layouts/doorkeeper/admin.html.erb +5 -3
  23. data/bin/console +16 -0
  24. data/config/locales/en.yml +1 -0
  25. data/doorkeeper.gemspec +28 -24
  26. data/gemfiles/rails_5_0.gemfile +9 -4
  27. data/gemfiles/rails_5_1.gemfile +9 -4
  28. data/gemfiles/rails_5_2.gemfile +10 -5
  29. data/gemfiles/rails_6_0.gemfile +17 -0
  30. data/gemfiles/rails_master.gemfile +9 -9
  31. data/lib/doorkeeper/config/option.rb +64 -0
  32. data/lib/doorkeeper/config.rb +197 -76
  33. data/lib/doorkeeper/engine.rb +3 -1
  34. data/lib/doorkeeper/errors.rb +17 -0
  35. data/lib/doorkeeper/grape/authorization_decorator.rb +6 -4
  36. data/lib/doorkeeper/grape/helpers.rb +5 -3
  37. data/lib/doorkeeper/helpers/controller.rb +5 -2
  38. data/lib/doorkeeper/models/access_grant_mixin.rb +25 -6
  39. data/lib/doorkeeper/models/access_token_mixin.rb +68 -22
  40. data/lib/doorkeeper/models/application_mixin.rb +45 -1
  41. data/lib/doorkeeper/models/concerns/accessible.rb +2 -0
  42. data/lib/doorkeeper/models/concerns/expirable.rb +6 -2
  43. data/lib/doorkeeper/models/concerns/orderable.rb +2 -0
  44. data/lib/doorkeeper/models/concerns/ownership.rb +3 -6
  45. data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
  46. data/lib/doorkeeper/models/concerns/revocable.rb +4 -1
  47. data/lib/doorkeeper/models/concerns/scopes.rb +7 -1
  48. data/lib/doorkeeper/models/concerns/secret_storable.rb +108 -0
  49. data/lib/doorkeeper/oauth/authorization/code.rb +4 -2
  50. data/lib/doorkeeper/oauth/authorization/context.rb +2 -0
  51. data/lib/doorkeeper/oauth/authorization/token.rb +14 -7
  52. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +3 -1
  53. data/lib/doorkeeper/oauth/authorization_code_request.rb +8 -4
  54. data/lib/doorkeeper/oauth/base_request.rb +2 -0
  55. data/lib/doorkeeper/oauth/base_response.rb +2 -0
  56. data/lib/doorkeeper/oauth/client/credentials.rb +2 -0
  57. data/lib/doorkeeper/oauth/client.rb +3 -1
  58. data/lib/doorkeeper/oauth/client_credentials/creator.rb +2 -0
  59. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +2 -0
  60. data/lib/doorkeeper/oauth/client_credentials/validation.rb +7 -4
  61. data/lib/doorkeeper/oauth/client_credentials_request.rb +3 -1
  62. data/lib/doorkeeper/oauth/code_request.rb +2 -0
  63. data/lib/doorkeeper/oauth/code_response.rb +4 -2
  64. data/lib/doorkeeper/oauth/error.rb +2 -0
  65. data/lib/doorkeeper/oauth/error_response.rb +20 -6
  66. data/lib/doorkeeper/oauth/forbidden_token_response.rb +10 -3
  67. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +25 -8
  68. data/lib/doorkeeper/oauth/helpers/unique_token.rb +15 -1
  69. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +42 -3
  70. data/lib/doorkeeper/oauth/invalid_token_response.rb +22 -0
  71. data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -2
  72. data/lib/doorkeeper/oauth/pre_authorization.rb +13 -5
  73. data/lib/doorkeeper/oauth/refresh_token_request.rb +15 -4
  74. data/lib/doorkeeper/oauth/scopes.rb +7 -3
  75. data/lib/doorkeeper/oauth/token.rb +4 -2
  76. data/lib/doorkeeper/oauth/token_introspection.rb +76 -8
  77. data/lib/doorkeeper/oauth/token_request.rb +2 -0
  78. data/lib/doorkeeper/oauth/token_response.rb +11 -9
  79. data/lib/doorkeeper/oauth.rb +5 -5
  80. data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -13
  81. data/lib/doorkeeper/orm/active_record/access_token.rb +6 -13
  82. data/lib/doorkeeper/orm/active_record/application.rb +26 -7
  83. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +10 -3
  84. data/lib/doorkeeper/orm/active_record.rb +8 -6
  85. data/lib/doorkeeper/rails/helpers.rb +5 -1
  86. data/lib/doorkeeper/rails/routes/mapper.rb +2 -0
  87. data/lib/doorkeeper/rails/routes/mapping.rb +9 -7
  88. data/lib/doorkeeper/rails/routes.rb +19 -12
  89. data/lib/doorkeeper/rake/db.rake +17 -17
  90. data/lib/doorkeeper/rake.rb +1 -1
  91. data/lib/doorkeeper/request/authorization_code.rb +2 -0
  92. data/lib/doorkeeper/request/client_credentials.rb +2 -0
  93. data/lib/doorkeeper/request/code.rb +2 -0
  94. data/lib/doorkeeper/request/password.rb +2 -0
  95. data/lib/doorkeeper/request/refresh_token.rb +2 -0
  96. data/lib/doorkeeper/request/strategy.rb +2 -0
  97. data/lib/doorkeeper/request/token.rb +2 -0
  98. data/lib/doorkeeper/request.rb +3 -1
  99. data/lib/doorkeeper/secret_storing/base.rb +64 -0
  100. data/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
  101. data/lib/doorkeeper/secret_storing/plain.rb +33 -0
  102. data/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
  103. data/lib/doorkeeper/server.rb +2 -0
  104. data/lib/doorkeeper/stale_records_cleaner.rb +20 -0
  105. data/lib/doorkeeper/validations.rb +2 -0
  106. data/lib/doorkeeper/version.rb +4 -2
  107. data/lib/doorkeeper.rb +69 -59
  108. data/lib/generators/doorkeeper/application_owner_generator.rb +10 -9
  109. data/lib/generators/doorkeeper/confidential_applications_generator.rb +10 -9
  110. data/lib/generators/doorkeeper/install_generator.rb +11 -9
  111. data/lib/generators/doorkeeper/migration_generator.rb +9 -9
  112. data/lib/generators/doorkeeper/pkce_generator.rb +10 -9
  113. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +10 -9
  114. data/lib/generators/doorkeeper/templates/initializer.rb +138 -13
  115. data/lib/generators/doorkeeper/templates/migration.rb.erb +17 -10
  116. data/lib/generators/doorkeeper/views_generator.rb +6 -4
  117. data/spec/controllers/application_metal_controller_spec.rb +24 -10
  118. data/spec/controllers/applications_controller_spec.rb +76 -78
  119. data/spec/controllers/authorizations_controller_spec.rb +189 -142
  120. data/spec/controllers/protected_resources_controller_spec.rb +111 -67
  121. data/spec/controllers/token_info_controller_spec.rb +14 -12
  122. data/spec/controllers/tokens_controller_spec.rb +170 -112
  123. data/spec/dummy/Rakefile +3 -1
  124. data/spec/dummy/app/controllers/application_controller.rb +3 -1
  125. data/spec/dummy/app/controllers/custom_authorizations_controller.rb +3 -1
  126. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -2
  127. data/spec/dummy/app/controllers/home_controller.rb +6 -5
  128. data/spec/dummy/app/controllers/metal_controller.rb +2 -0
  129. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +4 -2
  130. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  131. data/spec/dummy/app/models/user.rb +2 -0
  132. data/spec/dummy/config/application.rb +26 -2
  133. data/spec/dummy/config/boot.rb +2 -4
  134. data/spec/dummy/config/environment.rb +1 -1
  135. data/spec/dummy/config/environments/development.rb +2 -0
  136. data/spec/dummy/config/environments/production.rb +2 -0
  137. data/spec/dummy/config/environments/test.rb +4 -2
  138. data/spec/dummy/config/initializers/backtrace_silencers.rb +2 -0
  139. data/spec/dummy/config/initializers/doorkeeper.rb +7 -3
  140. data/spec/dummy/config/initializers/secret_token.rb +3 -1
  141. data/spec/dummy/config/initializers/session_store.rb +3 -1
  142. data/spec/dummy/config/initializers/wrap_parameters.rb +2 -0
  143. data/spec/dummy/config.ru +3 -1
  144. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +20 -13
  145. data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +1 -1
  146. data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +2 -0
  147. data/spec/dummy/db/schema.rb +1 -1
  148. data/spec/dummy/script/rails +7 -4
  149. data/spec/factories.rb +11 -9
  150. data/spec/generators/application_owner_generator_spec.rb +13 -26
  151. data/spec/generators/confidential_applications_generator_spec.rb +12 -28
  152. data/spec/generators/install_generator_spec.rb +19 -14
  153. data/spec/generators/migration_generator_spec.rb +13 -26
  154. data/spec/generators/pkce_generator_spec.rb +11 -26
  155. data/spec/generators/previous_refresh_token_generator_spec.rb +16 -29
  156. data/spec/generators/templates/routes.rb +2 -1
  157. data/spec/generators/views_generator_spec.rb +14 -12
  158. data/spec/grape/grape_integration_spec.rb +35 -33
  159. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +9 -7
  160. data/spec/lib/config_spec.rb +290 -104
  161. data/spec/lib/doorkeeper_spec.rb +8 -6
  162. data/spec/lib/models/expirable_spec.rb +22 -8
  163. data/spec/lib/models/reusable_spec.rb +40 -0
  164. data/spec/lib/models/revocable_spec.rb +8 -6
  165. data/spec/lib/models/scopes_spec.rb +27 -13
  166. data/spec/lib/models/secret_storable_spec.rb +135 -0
  167. data/spec/lib/oauth/authorization/uri_builder_spec.rb +17 -15
  168. data/spec/lib/oauth/authorization_code_request_spec.rb +43 -17
  169. data/spec/lib/oauth/base_request_spec.rb +24 -12
  170. data/spec/lib/oauth/base_response_spec.rb +3 -1
  171. data/spec/lib/oauth/client/credentials_spec.rb +26 -24
  172. data/spec/lib/oauth/client_credentials/creator_spec.rb +56 -10
  173. data/spec/lib/oauth/client_credentials/issuer_spec.rb +27 -20
  174. data/spec/lib/oauth/client_credentials/validation_spec.rb +21 -15
  175. data/spec/lib/oauth/client_credentials_integration_spec.rb +8 -6
  176. data/spec/lib/oauth/client_credentials_request_spec.rb +27 -21
  177. data/spec/lib/oauth/client_spec.rb +15 -13
  178. data/spec/lib/oauth/code_request_spec.rb +10 -8
  179. data/spec/lib/oauth/code_response_spec.rb +9 -7
  180. data/spec/lib/oauth/error_response_spec.rb +19 -11
  181. data/spec/lib/oauth/error_spec.rb +4 -2
  182. data/spec/lib/oauth/forbidden_token_response_spec.rb +7 -5
  183. data/spec/lib/oauth/helpers/scope_checker_spec.rb +68 -31
  184. data/spec/lib/oauth/helpers/unique_token_spec.rb +8 -6
  185. data/spec/lib/oauth/helpers/uri_checker_spec.rb +124 -93
  186. data/spec/lib/oauth/invalid_token_response_spec.rb +3 -1
  187. data/spec/lib/oauth/password_access_token_request_spec.rb +95 -34
  188. data/spec/lib/oauth/pre_authorization_spec.rb +80 -54
  189. data/spec/lib/oauth/refresh_token_request_spec.rb +46 -39
  190. data/spec/lib/oauth/scopes_spec.rb +63 -61
  191. data/spec/lib/oauth/token_request_spec.rb +80 -25
  192. data/spec/lib/oauth/token_response_spec.rb +40 -38
  193. data/spec/lib/oauth/token_spec.rb +64 -45
  194. data/spec/lib/request/strategy_spec.rb +3 -1
  195. data/spec/lib/secret_storing/base_spec.rb +60 -0
  196. data/spec/lib/secret_storing/bcrypt_spec.rb +49 -0
  197. data/spec/lib/secret_storing/plain_spec.rb +44 -0
  198. data/spec/lib/secret_storing/sha256_hash_spec.rb +48 -0
  199. data/spec/lib/server_spec.rb +20 -18
  200. data/spec/lib/{orm/active_record/stale_records_cleaner_spec.rb → stale_records_cleaner_spec.rb} +28 -18
  201. data/spec/models/doorkeeper/access_grant_spec.rb +80 -15
  202. data/spec/models/doorkeeper/access_token_spec.rb +212 -83
  203. data/spec/models/doorkeeper/application_spec.rb +146 -49
  204. data/spec/requests/applications/applications_request_spec.rb +100 -68
  205. data/spec/requests/applications/authorized_applications_spec.rb +20 -18
  206. data/spec/requests/endpoints/authorization_spec.rb +27 -25
  207. data/spec/requests/endpoints/token_spec.rb +38 -36
  208. data/spec/requests/flows/authorization_code_errors_spec.rb +27 -25
  209. data/spec/requests/flows/authorization_code_spec.rb +260 -158
  210. data/spec/requests/flows/client_credentials_spec.rb +54 -52
  211. data/spec/requests/flows/implicit_grant_errors_spec.rb +11 -9
  212. data/spec/requests/flows/implicit_grant_spec.rb +27 -25
  213. data/spec/requests/flows/password_spec.rb +64 -56
  214. data/spec/requests/flows/refresh_token_spec.rb +91 -32
  215. data/spec/requests/flows/revoke_token_spec.rb +49 -63
  216. data/spec/requests/flows/skip_authorization_spec.rb +23 -21
  217. data/spec/requests/protected_resources/metal_spec.rb +7 -5
  218. data/spec/requests/protected_resources/private_api_spec.rb +37 -35
  219. data/spec/routing/custom_controller_routes_spec.rb +71 -65
  220. data/spec/routing/default_routes_spec.rb +22 -20
  221. data/spec/routing/scoped_routes_spec.rb +20 -18
  222. data/spec/spec_helper.rb +15 -13
  223. data/spec/spec_helper_integration.rb +3 -1
  224. data/spec/support/dependencies/factory_bot.rb +3 -1
  225. data/spec/support/doorkeeper_rspec.rb +5 -2
  226. data/spec/support/helpers/access_token_request_helper.rb +3 -1
  227. data/spec/support/helpers/authorization_request_helper.rb +4 -2
  228. data/spec/support/helpers/config_helper.rb +2 -0
  229. data/spec/support/helpers/model_helper.rb +11 -5
  230. data/spec/support/helpers/request_spec_helper.rb +5 -3
  231. data/spec/support/helpers/url_helper.rb +20 -18
  232. data/spec/support/http_method_shim.rb +4 -9
  233. data/spec/support/orm/active_record.rb +3 -1
  234. data/spec/support/shared/controllers_shared_context.rb +68 -10
  235. data/spec/support/shared/hashing_shared_context.rb +36 -0
  236. data/spec/support/shared/models_shared_examples.rb +12 -10
  237. data/spec/validators/redirect_uri_validator_spec.rb +76 -47
  238. data/spec/version/version_spec.rb +7 -5
  239. metadata +70 -21
  240. data/gemfiles/rails_4_2.gemfile +0 -13
  241. data/spec/dummy/config/initializers/new_framework_defaults.rb +0 -10
@@ -1,286 +1,386 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
2
 
3
- feature 'Authorization Code Flow' do
3
+ require "spec_helper"
4
+
5
+ feature "Authorization Code Flow" do
4
6
  background do
5
- config_is_set(:authenticate_resource_owner) { User.first || redirect_to('/sign_in') }
7
+ config_is_set(:authenticate_resource_owner) { User.first || redirect_to("/sign_in") }
6
8
  client_exists
7
9
  create_resource_owner
8
10
  sign_in
9
11
  end
10
12
 
11
- scenario 'resource owner authorizes the client' do
13
+ scenario "resource owner authorizes the client" do
12
14
  visit authorization_endpoint_url(client: @client)
13
- click_on 'Authorize'
15
+ click_on "Authorize"
14
16
 
15
17
  access_grant_should_exist_for(@client, @resource_owner)
16
18
 
17
19
  i_should_be_on_client_callback(@client)
18
20
 
19
- url_should_have_param('code', Doorkeeper::AccessGrant.first.token)
20
- url_should_not_have_param('state')
21
- url_should_not_have_param('error')
21
+ url_should_have_param("code", Doorkeeper::AccessGrant.first.token)
22
+ url_should_not_have_param("state")
23
+ url_should_not_have_param("error")
22
24
  end
23
25
 
24
- scenario 'resource owner authorizes using test url' do
26
+ context "with grant hashing enabled" do
27
+ background do
28
+ config_is_set(:token_secret_strategy, ::Doorkeeper::SecretStoring::Sha256Hash)
29
+ end
30
+
31
+ scenario "Authorization Code Flow with hashing" do
32
+ @client.redirect_uri = Doorkeeper.configuration.native_redirect_uri
33
+ @client.save!
34
+ visit authorization_endpoint_url(client: @client)
35
+ click_on "Authorize"
36
+
37
+ access_grant_should_exist_for(@client, @resource_owner)
38
+
39
+ code = current_params["code"]
40
+ expect(code).not_to be_nil
41
+
42
+ hashed_code = Doorkeeper::AccessGrant.secret_strategy.transform_secret code
43
+ expect(hashed_code).to eq Doorkeeper::AccessGrant.first.token
44
+
45
+ expect(code).not_to eq(hashed_code)
46
+
47
+ i_should_see "Authorization code:"
48
+ i_should_see code
49
+ i_should_not_see hashed_code
50
+ end
51
+ end
52
+
53
+ scenario "resource owner authorizes using test url" do
25
54
  @client.redirect_uri = Doorkeeper.configuration.native_redirect_uri
26
55
  @client.save!
27
56
  visit authorization_endpoint_url(client: @client)
28
- click_on 'Authorize'
57
+ click_on "Authorize"
29
58
 
30
59
  access_grant_should_exist_for(@client, @resource_owner)
31
60
 
32
- url_should_have_param('code', Doorkeeper::AccessGrant.first.token)
33
- i_should_see 'Authorization code:'
61
+ url_should_have_param("code", Doorkeeper::AccessGrant.first.token)
62
+ i_should_see "Authorization code:"
34
63
  i_should_see Doorkeeper::AccessGrant.first.token
35
64
  end
36
65
 
37
- scenario 'resource owner authorizes the client with state parameter set' do
38
- visit authorization_endpoint_url(client: @client, state: 'return-me')
39
- click_on 'Authorize'
40
- url_should_have_param('code', Doorkeeper::AccessGrant.first.token)
41
- url_should_have_param('state', 'return-me')
42
- url_should_not_have_param('code_challenge_method')
66
+ scenario "resource owner authorizes the client with state parameter set" do
67
+ visit authorization_endpoint_url(client: @client, state: "return-me")
68
+ click_on "Authorize"
69
+ url_should_have_param("code", Doorkeeper::AccessGrant.first.token)
70
+ url_should_have_param("state", "return-me")
71
+ url_should_not_have_param("code_challenge_method")
43
72
  end
44
73
 
45
- scenario 'resource owner requests an access token with authorization code' do
74
+ scenario "resource owner requests an access token with authorization code" do
46
75
  visit authorization_endpoint_url(client: @client)
47
- click_on 'Authorize'
76
+ click_on "Authorize"
48
77
 
49
78
  authorization_code = Doorkeeper::AccessGrant.first.token
50
79
  create_access_token authorization_code, @client
51
80
 
52
81
  access_token_should_exist_for(@client, @resource_owner)
53
82
 
54
- should_not_have_json 'error'
83
+ should_not_have_json "error"
55
84
 
56
- should_have_json 'access_token', Doorkeeper::AccessToken.first.token
57
- should_have_json 'token_type', 'Bearer'
58
- should_have_json_within 'expires_in', Doorkeeper::AccessToken.first.expires_in, 1
85
+ should_have_json "access_token", Doorkeeper::AccessToken.first.token
86
+ should_have_json "token_type", "Bearer"
87
+ should_have_json_within "expires_in", Doorkeeper::AccessToken.first.expires_in, 1
59
88
  end
60
89
 
61
- scenario 'resource owner requests an access token with authorization code but without secret' do
90
+ scenario "resource owner requests an access token with authorization code but without secret" do
62
91
  visit authorization_endpoint_url(client: @client)
63
- click_on 'Authorize'
92
+ click_on "Authorize"
64
93
 
65
94
  authorization_code = Doorkeeper::AccessGrant.first.token
66
95
  page.driver.post token_endpoint_url(code: authorization_code, client_id: @client.uid,
67
96
  redirect_uri: @client.redirect_uri)
68
97
 
69
- expect(Doorkeeper::AccessToken).not_to exist
98
+ expect(Doorkeeper::AccessToken.count).to be_zero
99
+
100
+ should_have_json "error", "invalid_client"
101
+ end
102
+
103
+ scenario "resource owner requests an access token with authorization code but without client id" do
104
+ visit authorization_endpoint_url(client: @client)
105
+ click_on "Authorize"
106
+
107
+ authorization_code = Doorkeeper::AccessGrant.first.token
108
+ page.driver.post token_endpoint_url(code: authorization_code, client_secret: @client.secret,
109
+ redirect_uri: @client.redirect_uri)
110
+
111
+ expect(Doorkeeper::AccessToken.count).to be_zero
70
112
 
71
- should_have_json 'error', 'invalid_client'
113
+ should_have_json "error", "invalid_client"
72
114
  end
73
115
 
74
- context 'with PKCE' do
75
- context 'plain' do
76
- let(:code_challenge) { 'a45a9fea-0676-477e-95b1-a40f72ac3cfb' }
77
- let(:code_verifier) { 'a45a9fea-0676-477e-95b1-a40f72ac3cfb' }
116
+ scenario "silently authorizes if matching token exists" do
117
+ default_scopes_exist :public, :write
78
118
 
79
- scenario 'resource owner authorizes the client with code_challenge parameter set' do
80
- visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'plain')
81
- click_on 'Authorize'
119
+ access_token_exists application: @client,
120
+ expires_in: -100, # even expired token
121
+ resource_owner_id: @resource_owner.id,
122
+ scopes: "public write"
82
123
 
83
- url_should_have_param('code', Doorkeeper::AccessGrant.first.token)
84
- url_should_not_have_param('code_challenge_method')
85
- url_should_not_have_param('code_challenge')
124
+ visit authorization_endpoint_url(client: @client, scope: "public write")
125
+
126
+ response_status_should_be 200
127
+ i_should_not_see "Authorize"
128
+ end
129
+
130
+ context "with PKCE" do
131
+ context "plain" do
132
+ let(:code_challenge) { "a45a9fea-0676-477e-95b1-a40f72ac3cfb" }
133
+ let(:code_verifier) { "a45a9fea-0676-477e-95b1-a40f72ac3cfb" }
134
+
135
+ scenario "resource owner authorizes the client with code_challenge parameter set" do
136
+ visit authorization_endpoint_url(
137
+ client: @client,
138
+ code_challenge: code_challenge,
139
+ code_challenge_method: "plain"
140
+ )
141
+ click_on "Authorize"
142
+
143
+ url_should_have_param("code", Doorkeeper::AccessGrant.first.token)
144
+ url_should_not_have_param("code_challenge_method")
145
+ url_should_not_have_param("code_challenge")
86
146
  end
87
147
 
88
- scenario 'mobile app requests an access token with authorization code but not pkce token' do
148
+ scenario "mobile app requests an access token with authorization code but not pkce token" do
89
149
  visit authorization_endpoint_url(client: @client)
90
- click_on 'Authorize'
150
+ click_on "Authorize"
91
151
 
92
- authorization_code = current_params['code']
152
+ authorization_code = current_params["code"]
93
153
  create_access_token authorization_code, @client, code_verifier
94
154
 
95
- should_have_json 'error', 'invalid_grant'
155
+ should_have_json "error", "invalid_grant"
96
156
  end
97
157
 
98
- scenario 'mobile app requests an access token with authorization code and plain code challenge method' do
99
- visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'plain')
100
- click_on 'Authorize'
158
+ scenario "mobile app requests an access token with authorization code and plain code challenge method" do
159
+ visit authorization_endpoint_url(
160
+ client: @client,
161
+ code_challenge: code_challenge,
162
+ code_challenge_method: "plain"
163
+ )
164
+ click_on "Authorize"
101
165
 
102
- authorization_code = current_params['code']
166
+ authorization_code = current_params["code"]
103
167
  create_access_token authorization_code, @client, code_verifier
104
168
 
105
169
  access_token_should_exist_for(@client, @resource_owner)
106
170
 
107
- should_not_have_json 'error'
171
+ should_not_have_json "error"
108
172
 
109
- should_have_json 'access_token', Doorkeeper::AccessToken.first.token
110
- should_have_json 'token_type', 'Bearer'
111
- should_have_json_within 'expires_in', Doorkeeper::AccessToken.first.expires_in, 1
173
+ should_have_json "access_token", Doorkeeper::AccessToken.first.token
174
+ should_have_json "token_type", "Bearer"
175
+ should_have_json_within "expires_in", Doorkeeper::AccessToken.first.expires_in, 1
112
176
  end
113
177
 
114
- scenario 'mobile app requests an access token with authorization code and code_challenge' do
178
+ scenario "mobile app requests an access token with authorization code and code_challenge" do
115
179
  visit authorization_endpoint_url(client: @client,
116
180
  code_challenge: code_verifier,
117
- code_challenge_method: 'plain')
118
- click_on 'Authorize'
181
+ code_challenge_method: "plain")
182
+ click_on "Authorize"
119
183
 
120
- authorization_code = current_params['code']
184
+ authorization_code = current_params["code"]
121
185
  create_access_token authorization_code, @client, code_verifier: nil
122
186
 
123
- should_not_have_json 'access_token'
124
- should_have_json 'error', 'invalid_grant'
187
+ should_not_have_json "access_token"
188
+ should_have_json "error", "invalid_grant"
125
189
  end
126
190
  end
127
191
 
128
- context 's256' do
129
- let(:code_challenge) { 'Oz733NtQ0rJP8b04fgZMJMwprn6Iw8sMCT_9bR1q4tA' }
130
- let(:code_verifier) { 'a45a9fea-0676-477e-95b1-a40f72ac3cfb' }
131
-
132
- scenario 'resource owner authorizes the client with code_challenge parameter set' do
133
- visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
134
- click_on 'Authorize'
135
-
136
- url_should_have_param('code', Doorkeeper::AccessGrant.first.token)
137
- url_should_not_have_param('code_challenge_method')
138
- url_should_not_have_param('code_challenge')
192
+ context "s256" do
193
+ let(:code_challenge) { "Oz733NtQ0rJP8b04fgZMJMwprn6Iw8sMCT_9bR1q4tA" }
194
+ let(:code_verifier) { "a45a9fea-0676-477e-95b1-a40f72ac3cfb" }
195
+
196
+ scenario "resource owner authorizes the client with code_challenge parameter set" do
197
+ visit authorization_endpoint_url(
198
+ client: @client,
199
+ code_challenge: code_challenge,
200
+ code_challenge_method: "S256"
201
+ )
202
+ click_on "Authorize"
203
+
204
+ url_should_have_param("code", Doorkeeper::AccessGrant.first.token)
205
+ url_should_not_have_param("code_challenge_method")
206
+ url_should_not_have_param("code_challenge")
139
207
  end
140
208
 
141
- scenario 'mobile app requests an access token with authorization code and S256 code challenge method' do
142
- visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
143
- click_on 'Authorize'
209
+ scenario "mobile app requests an access token with authorization code and S256 code challenge method" do
210
+ visit authorization_endpoint_url(
211
+ client: @client,
212
+ code_challenge: code_challenge,
213
+ code_challenge_method: "S256"
214
+ )
215
+ click_on "Authorize"
144
216
 
145
- authorization_code = current_params['code']
217
+ authorization_code = current_params["code"]
146
218
  create_access_token authorization_code, @client, code_verifier
147
219
 
148
220
  access_token_should_exist_for(@client, @resource_owner)
149
221
 
150
- should_not_have_json 'error'
222
+ should_not_have_json "error"
151
223
 
152
- should_have_json 'access_token', Doorkeeper::AccessToken.first.token
153
- should_have_json 'token_type', 'Bearer'
154
- should_have_json_within 'expires_in', Doorkeeper::AccessToken.first.expires_in, 1
224
+ should_have_json "access_token", Doorkeeper::AccessToken.first.token
225
+ should_have_json "token_type", "Bearer"
226
+ should_have_json_within "expires_in", Doorkeeper::AccessToken.first.expires_in, 1
155
227
  end
156
228
 
157
- scenario 'mobile app requests an access token with authorization code and without code_verifier' do
158
- visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
159
- click_on 'Authorize'
160
- authorization_code = current_params['code']
229
+ scenario "mobile app requests an access token with authorization code and without code_verifier" do
230
+ visit authorization_endpoint_url(
231
+ client: @client,
232
+ code_challenge: code_challenge,
233
+ code_challenge_method: "S256"
234
+ )
235
+ click_on "Authorize"
236
+ authorization_code = current_params["code"]
161
237
  create_access_token authorization_code, @client
162
- should_have_json 'error', 'invalid_request'
163
- should_not_have_json 'access_token'
238
+ should_have_json "error", "invalid_request"
239
+ should_not_have_json "access_token"
164
240
  end
165
241
 
166
- scenario 'mobile app requests an access token with authorization code and without secret' do
167
- visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
168
- click_on 'Authorize'
242
+ scenario "mobile app requests an access token with authorization code and without secret" do
243
+ visit authorization_endpoint_url(
244
+ client: @client,
245
+ code_challenge: code_challenge,
246
+ code_challenge_method: "S256"
247
+ )
248
+ click_on "Authorize"
169
249
 
170
- authorization_code = current_params['code']
250
+ authorization_code = current_params["code"]
171
251
  page.driver.post token_endpoint_url(code: authorization_code, client_id: @client.uid,
172
252
  redirect_uri: @client.redirect_uri, code_verifier: code_verifier)
173
- should_have_json 'error', 'invalid_client'
174
- should_not_have_json 'access_token'
253
+ should_have_json "error", "invalid_client"
254
+ should_not_have_json "access_token"
175
255
  end
176
256
 
177
- scenario 'mobile app requests an access token with authorization code and without secret but is marked as not confidential' do
257
+ scenario "mobile app requests an access token with authorization code and without secret but is marked as not confidential" do
178
258
  @client.update_attribute :confidential, false
179
- visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
180
- click_on 'Authorize'
181
-
182
- authorization_code = current_params['code']
183
- page.driver.post token_endpoint_url(code: authorization_code, client_id: @client.uid,
184
- redirect_uri: @client.redirect_uri, code_verifier: code_verifier)
185
- should_not_have_json 'error'
186
-
187
- should_have_json 'access_token', Doorkeeper::AccessToken.first.token
188
- should_have_json 'token_type', 'Bearer'
189
- should_have_json_within 'expires_in', Doorkeeper::AccessToken.first.expires_in, 1
259
+ visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: "S256")
260
+ click_on "Authorize"
261
+
262
+ authorization_code = current_params["code"]
263
+ page.driver.post token_endpoint_url(
264
+ code: authorization_code,
265
+ client_id: @client.uid,
266
+ redirect_uri: @client.redirect_uri,
267
+ code_verifier: code_verifier
268
+ )
269
+ should_not_have_json "error"
270
+
271
+ should_have_json "access_token", Doorkeeper::AccessToken.first.token
272
+ should_have_json "token_type", "Bearer"
273
+ should_have_json_within "expires_in", Doorkeeper::AccessToken.first.expires_in, 1
190
274
  end
191
275
 
192
- scenario 'mobile app requests an access token with authorization code but no code verifier' do
193
- visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
194
- click_on 'Authorize'
276
+ scenario "mobile app requests an access token with authorization code but no code verifier" do
277
+ visit authorization_endpoint_url(
278
+ client: @client,
279
+ code_challenge: code_challenge,
280
+ code_challenge_method: "S256"
281
+ )
282
+ click_on "Authorize"
195
283
 
196
- authorization_code = current_params['code']
284
+ authorization_code = current_params["code"]
197
285
  create_access_token authorization_code, @client
198
286
 
199
- should_not_have_json 'access_token'
200
- should_have_json 'error', 'invalid_request'
287
+ should_not_have_json "access_token"
288
+ should_have_json "error", "invalid_request"
201
289
  end
202
290
 
203
- scenario 'mobile app requests an access token with authorization code with wrong verifier' do
204
- visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
205
- click_on 'Authorize'
291
+ scenario "mobile app requests an access token with authorization code with wrong verifier" do
292
+ visit authorization_endpoint_url(
293
+ client: @client,
294
+ code_challenge: code_challenge,
295
+ code_challenge_method: "S256"
296
+ )
297
+ click_on "Authorize"
206
298
 
207
- authorization_code = current_params['code']
208
- create_access_token authorization_code, @client, 'incorrect-code-verifier'
299
+ authorization_code = current_params["code"]
300
+ create_access_token authorization_code, @client, "incorrect-code-verifier"
209
301
 
210
- should_not_have_json 'access_token'
211
- should_have_json 'error', 'invalid_grant'
302
+ should_not_have_json "access_token"
303
+ should_have_json "error", "invalid_grant"
212
304
  end
213
305
 
214
- scenario 'code_challenge_mehthod in token request is totally ignored' do
215
- visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
216
- click_on 'Authorize'
217
-
218
- authorization_code = current_params['code']
219
- page.driver.post token_endpoint_url(code: authorization_code, client: @client, code_verifier: code_challenge,
220
- code_challenge_method: 'plain')
221
-
222
- should_not_have_json 'access_token'
223
- should_have_json 'error', 'invalid_grant'
306
+ scenario "code_challenge_mehthod in token request is totally ignored" do
307
+ visit authorization_endpoint_url(
308
+ client: @client,
309
+ code_challenge: code_challenge,
310
+ code_challenge_method: "S256"
311
+ )
312
+ click_on "Authorize"
313
+
314
+ authorization_code = current_params["code"]
315
+ page.driver.post token_endpoint_url(
316
+ code: authorization_code,
317
+ client: @client,
318
+ code_verifier: code_challenge,
319
+ code_challenge_method: "plain"
320
+ )
321
+
322
+ should_not_have_json "access_token"
323
+ should_have_json "error", "invalid_grant"
224
324
  end
225
325
 
226
- scenario 'expects to set code_challenge_method explicitely without fallback' do
326
+ scenario "expects to set code_challenge_method explicitely without fallback" do
227
327
  visit authorization_endpoint_url(client: @client, code_challenge: code_challenge)
228
- expect(page).to have_content('The code challenge method must be plain or S256.')
328
+ expect(page).to have_content("The code challenge method must be plain or S256.")
229
329
  end
230
330
  end
231
331
  end
232
332
 
233
- context 'when application scopes are present and no scope is passed' do
333
+ context "when application scopes are present and no scope is passed" do
234
334
  background do
235
- @client.update_attributes(scopes: 'public write read')
335
+ @client.update(scopes: "public write read")
236
336
  end
237
337
 
238
- scenario 'access grant has no scope' do
338
+ scenario "access grant has no scope" do
239
339
  default_scopes_exist :admin
240
340
  visit authorization_endpoint_url(client: @client)
241
- click_on 'Authorize'
341
+ click_on "Authorize"
242
342
  access_grant_should_exist_for(@client, @resource_owner)
243
343
  grant = Doorkeeper::AccessGrant.first
244
344
  expect(grant.scopes).to be_empty
245
345
  end
246
346
 
247
- scenario 'access grant have scopes which are common in application scopees and default scopes' do
347
+ scenario "access grant have scopes which are common in application scopees and default scopes" do
248
348
  default_scopes_exist :public, :write
249
349
  visit authorization_endpoint_url(client: @client)
250
- click_on 'Authorize'
350
+ click_on "Authorize"
251
351
  access_grant_should_exist_for(@client, @resource_owner)
252
352
  access_grant_should_have_scopes :public, :write
253
353
  end
254
354
  end
255
355
 
256
- context 'with scopes' do
356
+ context "with scopes" do
257
357
  background do
258
358
  default_scopes_exist :public
259
359
  optional_scopes_exist :write
260
360
  end
261
361
 
262
- scenario 'resource owner authorizes the client with default scopes' do
362
+ scenario "resource owner authorizes the client with default scopes" do
263
363
  visit authorization_endpoint_url(client: @client)
264
- click_on 'Authorize'
364
+ click_on "Authorize"
265
365
  access_grant_should_exist_for(@client, @resource_owner)
266
366
  access_grant_should_have_scopes :public
267
367
  end
268
368
 
269
- scenario 'resource owner authorizes the client with required scopes' do
270
- visit authorization_endpoint_url(client: @client, scope: 'public write')
271
- click_on 'Authorize'
369
+ scenario "resource owner authorizes the client with required scopes" do
370
+ visit authorization_endpoint_url(client: @client, scope: "public write")
371
+ click_on "Authorize"
272
372
  access_grant_should_have_scopes :public, :write
273
373
  end
274
374
 
275
- scenario 'resource owner authorizes the client with required scopes (without defaults)' do
276
- visit authorization_endpoint_url(client: @client, scope: 'write')
277
- click_on 'Authorize'
375
+ scenario "resource owner authorizes the client with required scopes (without defaults)" do
376
+ visit authorization_endpoint_url(client: @client, scope: "write")
377
+ click_on "Authorize"
278
378
  access_grant_should_have_scopes :write
279
379
  end
280
380
 
281
- scenario 'new access token matches required scopes' do
282
- visit authorization_endpoint_url(client: @client, scope: 'public write')
283
- click_on 'Authorize'
381
+ scenario "new access token matches required scopes" do
382
+ visit authorization_endpoint_url(client: @client, scope: "public write")
383
+ click_on "Authorize"
284
384
 
285
385
  authorization_code = Doorkeeper::AccessGrant.first.token
286
386
  create_access_token authorization_code, @client
@@ -289,57 +389,59 @@ feature 'Authorization Code Flow' do
289
389
  access_token_should_have_scopes :public, :write
290
390
  end
291
391
 
292
- scenario 'returns new token if scopes have changed' do
293
- client_is_authorized(@client, @resource_owner, scopes: 'public write')
294
- visit authorization_endpoint_url(client: @client, scope: 'public')
295
- click_on 'Authorize'
392
+ scenario "returns new token if scopes have changed" do
393
+ client_is_authorized(@client, @resource_owner, scopes: "public write")
394
+ visit authorization_endpoint_url(client: @client, scope: "public")
395
+ click_on "Authorize"
296
396
 
297
397
  authorization_code = Doorkeeper::AccessGrant.first.token
298
398
  create_access_token authorization_code, @client
299
399
 
300
400
  expect(Doorkeeper::AccessToken.count).to be(2)
301
401
 
302
- should_have_json 'access_token', Doorkeeper::AccessToken.last.token
402
+ should_have_json "access_token", Doorkeeper::AccessToken.last.token
303
403
  end
304
404
 
305
- scenario 'resource owner authorizes the client with extra scopes' do
306
- client_is_authorized(@client, @resource_owner, scopes: 'public')
307
- visit authorization_endpoint_url(client: @client, scope: 'public write')
308
- click_on 'Authorize'
405
+ scenario "resource owner authorizes the client with extra scopes" do
406
+ client_is_authorized(@client, @resource_owner, scopes: "public")
407
+ visit authorization_endpoint_url(client: @client, scope: "public write")
408
+ click_on "Authorize"
309
409
 
310
410
  authorization_code = Doorkeeper::AccessGrant.first.token
311
411
  create_access_token authorization_code, @client
312
412
 
313
413
  expect(Doorkeeper::AccessToken.count).to be(2)
314
414
 
315
- should_have_json 'access_token', Doorkeeper::AccessToken.last.token
415
+ should_have_json "access_token", Doorkeeper::AccessToken.last.token
316
416
  access_token_should_have_scopes :public, :write
317
417
  end
318
418
  end
319
419
  end
320
420
 
321
- describe 'Authorization Code Flow' do
421
+ describe "Authorization Code Flow" do
322
422
  before do
323
423
  Doorkeeper.configure do
324
424
  orm DOORKEEPER_ORM
325
425
  use_refresh_token
326
426
  end
427
+
327
428
  client_exists
328
429
  end
329
430
 
330
- context 'issuing a refresh token' do
431
+ context "issuing a refresh token" do
331
432
  before do
332
433
  authorization_code_exists application: @client
333
434
  end
334
435
 
335
- it 'second of simultaneous client requests get an error for revoked acccess token' do
436
+ it "second of simultaneous client requests get an error for revoked acccess token" do
336
437
  authorization_code = Doorkeeper::AccessGrant.first.token
337
- allow_any_instance_of(Doorkeeper::AccessGrant).to receive(:revoked?).and_return(false, true)
438
+ allow_any_instance_of(Doorkeeper::AccessGrant)
439
+ .to receive(:revoked?).and_return(false, true)
338
440
 
339
441
  post token_endpoint_url(code: authorization_code, client: @client)
340
442
 
341
- should_not_have_json 'access_token'
342
- should_have_json 'error', 'invalid_grant'
443
+ should_not_have_json "access_token"
444
+ should_have_json "error", "invalid_grant"
343
445
  end
344
446
  end
345
447
  end