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
@@ -25,8 +25,8 @@ end
25
25
 
26
26
  feature 'Listing applications' do
27
27
  background do
28
- FactoryGirl.create :application, name: 'Oauth Dude'
29
- FactoryGirl.create :application, name: 'Awesome App'
28
+ FactoryBot.create :application, name: 'Oauth Dude'
29
+ FactoryBot.create :application, name: 'Awesome App'
30
30
  end
31
31
 
32
32
  scenario 'application list' do
@@ -38,7 +38,7 @@ end
38
38
 
39
39
  feature 'Show application' do
40
40
  given :app do
41
- FactoryGirl.create :application, name: 'Just another oauth app'
41
+ FactoryBot.create :application, name: 'Just another oauth app'
42
42
  end
43
43
 
44
44
  scenario 'visiting application page' do
@@ -49,7 +49,7 @@ end
49
49
 
50
50
  feature 'Edit application' do
51
51
  let :app do
52
- FactoryGirl.create :application, name: 'OMG my app'
52
+ FactoryBot.create :application, name: 'OMG my app'
53
53
  end
54
54
 
55
55
  background do
@@ -73,7 +73,7 @@ end
73
73
 
74
74
  feature 'Remove application' do
75
75
  background do
76
- @app = FactoryGirl.create :application
76
+ @app = FactoryBot.create :application
77
77
  end
78
78
 
79
79
  scenario 'deleting an application from list' do
@@ -59,13 +59,12 @@ feature 'Authorization endpoint' do
59
59
  end
60
60
 
61
61
  scenario 'raises exception on forged requests' do
62
- skip 'TODO: need to add request helpers to this feature spec'
63
- allow_any_instance_of(ActionController::Base).to receive(:handle_unverified_request)
64
62
  allowing_forgery_protection do
65
- post "/oauth/authorize",
66
- client_id: @client.uid,
67
- redirect_uri: @client.redirect_uri,
68
- response_type: 'code'
63
+ expect {
64
+ page.driver.post authorization_endpoint_url(client_id: @client.uid,
65
+ redirect_uri: @client.redirect_uri,
66
+ response_type: 'code')
67
+ }.to raise_error(ActionController::InvalidAuthenticityToken)
69
68
  end
70
69
  end
71
70
  end
@@ -9,7 +9,14 @@ describe 'Token endpoint' do
9
9
  it 'respond with correct headers' do
10
10
  post token_endpoint_url(code: @authorization.token, client: @client)
11
11
  should_have_header 'Pragma', 'no-cache'
12
- should_have_header 'Cache-Control', 'no-store'
12
+
13
+ # Rails 5.2 changed headers
14
+ if ::Rails::VERSION::MAJOR >= 5 && ::Rails::VERSION::MINOR >= 2 || ::Rails::VERSION::MAJOR >= 6
15
+ should_have_header 'Cache-Control', 'private, no-store'
16
+ else
17
+ should_have_header 'Cache-Control', 'no-store'
18
+ end
19
+
13
20
  should_have_header 'Content-Type', 'application/json; charset=utf-8'
14
21
  end
15
22
 
@@ -1,9 +1,10 @@
1
1
  require 'spec_helper_integration'
2
2
 
3
3
  feature 'Authorization Code Flow Errors' do
4
+ let(:client_params) { {} }
4
5
  background do
5
6
  config_is_set(:authenticate_resource_owner) { User.first || redirect_to('/sign_in') }
6
- client_exists
7
+ client_exists client_params
7
8
  create_resource_owner
8
9
  sign_in
9
10
  end
@@ -12,6 +13,15 @@ feature 'Authorization Code Flow Errors' do
12
13
  access_grant_should_not_exist
13
14
  end
14
15
 
16
+ context "with a client trying to xss resource owner" do
17
+ let(:client_name) { "<div id='xss'>XSS</div>" }
18
+ let(:client_params) { { name: client_name } }
19
+ scenario "resource owner visit authorization endpoint" do
20
+ visit authorization_endpoint_url(client: @client)
21
+ expect(page).not_to have_css("#xss")
22
+ end
23
+ end
24
+
15
25
  context 'when access was denied' do
16
26
  scenario 'redirects with error' do
17
27
  visit authorization_endpoint_url(client: @client)
@@ -29,6 +29,7 @@ feature 'Authorization Code Flow' do
29
29
 
30
30
  access_grant_should_exist_for(@client, @resource_owner)
31
31
 
32
+ url_should_have_param('code', Doorkeeper::AccessGrant.first.token)
32
33
  i_should_see 'Authorization code:'
33
34
  i_should_see Doorkeeper::AccessGrant.first.token
34
35
  end
@@ -41,20 +42,18 @@ feature 'Authorization Code Flow' do
41
42
  end
42
43
 
43
44
  scenario 'resource owner requests an access token with authorization code' do
44
- skip 'TODO: need to add request helpers to this feature spec'
45
-
46
45
  visit authorization_endpoint_url(client: @client)
47
46
  click_on 'Authorize'
48
47
 
49
48
  authorization_code = Doorkeeper::AccessGrant.first.token
50
- post token_endpoint_url(code: authorization_code, client: @client)
49
+ create_access_token authorization_code, @client
51
50
 
52
51
  access_token_should_exist_for(@client, @resource_owner)
53
52
 
54
53
  should_not_have_json 'error'
55
54
 
56
55
  should_have_json 'access_token', Doorkeeper::AccessToken.first.token
57
- should_have_json 'token_type', 'bearer'
56
+ should_have_json 'token_type', 'Bearer'
58
57
  should_have_json_within 'expires_in', Doorkeeper::AccessToken.first.expires_in, 1
59
58
  end
60
59
 
@@ -84,27 +83,23 @@ feature 'Authorization Code Flow' do
84
83
  end
85
84
 
86
85
  scenario 'new access token matches required scopes' do
87
- skip 'TODO: need to add request helpers to this feature spec'
88
-
89
86
  visit authorization_endpoint_url(client: @client, scope: 'public write')
90
87
  click_on 'Authorize'
91
88
 
92
89
  authorization_code = Doorkeeper::AccessGrant.first.token
93
- post token_endpoint_url(code: authorization_code, client: @client)
90
+ create_access_token authorization_code, @client
94
91
 
95
92
  access_token_should_exist_for(@client, @resource_owner)
96
93
  access_token_should_have_scopes :public, :write
97
94
  end
98
95
 
99
96
  scenario 'returns new token if scopes have changed' do
100
- skip 'TODO: need to add request helpers to this feature spec'
101
-
102
97
  client_is_authorized(@client, @resource_owner, scopes: 'public write')
103
98
  visit authorization_endpoint_url(client: @client, scope: 'public')
104
99
  click_on 'Authorize'
105
100
 
106
101
  authorization_code = Doorkeeper::AccessGrant.first.token
107
- post token_endpoint_url(code: authorization_code, client: @client)
102
+ create_access_token authorization_code, @client
108
103
 
109
104
  expect(Doorkeeper::AccessToken.count).to be(2)
110
105
 
@@ -112,14 +107,12 @@ feature 'Authorization Code Flow' do
112
107
  end
113
108
 
114
109
  scenario 'resource owner authorizes the client with extra scopes' do
115
- skip 'TODO: need to add request helpers to this feature spec'
116
-
117
110
  client_is_authorized(@client, @resource_owner, scopes: 'public')
118
111
  visit authorization_endpoint_url(client: @client, scope: 'public write')
119
112
  click_on 'Authorize'
120
113
 
121
114
  authorization_code = Doorkeeper::AccessGrant.first.token
122
- post token_endpoint_url(code: authorization_code, client: @client)
115
+ create_access_token authorization_code, @client
123
116
 
124
117
  expect(Doorkeeper::AccessToken.count).to be(2)
125
118
 
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper_integration'
2
2
 
3
3
  describe 'Client Credentials Request' do
4
- let(:client) { FactoryGirl.create :application }
4
+ let(:client) { FactoryBot.create :application }
5
5
 
6
6
  context 'a valid request' do
7
7
  it 'authorizes the client and returns the token response' do
@@ -22,6 +22,7 @@ describe 'Client Credentials Request' do
22
22
  context 'with scopes' do
23
23
  before do
24
24
  optional_scopes_exist :write
25
+ default_scopes_exist :public
25
26
  end
26
27
 
27
28
  it 'adds the scope to the token an returns in the response' do
@@ -33,6 +34,33 @@ describe 'Client Credentials Request' do
33
34
  should_have_json 'access_token', Doorkeeper::AccessToken.first.token
34
35
  should_have_json 'scope', 'write'
35
36
  end
37
+
38
+ context 'that are default' do
39
+ it 'adds the scope to the token an returns in the response' do
40
+ headers = authorization client.uid, client.secret
41
+ params = { grant_type: 'client_credentials', scope: 'public' }
42
+
43
+ post '/oauth/token', params, headers
44
+
45
+ should_have_json 'access_token', Doorkeeper::AccessToken.first.token
46
+ should_have_json 'scope', 'public'
47
+ end
48
+ end
49
+
50
+ context 'that are invalid' do
51
+ it 'does not authorize the client and returns the error' do
52
+ headers = authorization client.uid, client.secret
53
+ params = { grant_type: 'client_credentials', scope: 'random' }
54
+
55
+ post '/oauth/token', params, headers
56
+
57
+ should_have_json 'error', 'invalid_scope'
58
+ should_have_json 'error_description', translated_error_message(:invalid_scope)
59
+ should_not_have_json 'access_token'
60
+
61
+ expect(response.status).to eq(401)
62
+ end
63
+ end
36
64
  end
37
65
  end
38
66
 
@@ -17,13 +17,13 @@ feature 'Implicit Grant Flow Errors' do
17
17
  [:client_id, :invalid_client],
18
18
  [:redirect_uri, :invalid_redirect_uri]
19
19
  ].each do |error|
20
- scenario "displays #{error.last.inspect} error for invalid #{error.first.inspect}" do
20
+ scenario "displays #{error.last} error for invalid #{error.first}" do
21
21
  visit authorization_endpoint_url(client: @client, error.first => 'invalid', response_type: 'token')
22
22
  i_should_not_see 'Authorize'
23
23
  i_should_see_translated_error_message error.last
24
24
  end
25
25
 
26
- scenario "displays #{error.last.inspect} error when #{error.first.inspect} is missing" do
26
+ scenario "displays #{error.last} error when #{error.first} is missing" do
27
27
  visit authorization_endpoint_url(client: @client, error.first => '', response_type: 'token')
28
28
  i_should_not_see 'Authorize'
29
29
  i_should_see_translated_error_message error.last
@@ -10,38 +10,94 @@ describe 'Resource Owner Password Credentials Flow not set up' do
10
10
  it 'doesn\'t issue new token' do
11
11
  expect do
12
12
  post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)
13
- end.to_not change { Doorkeeper::AccessToken.count }
13
+ end.to_not(change { Doorkeeper::AccessToken.count })
14
14
  end
15
15
  end
16
16
  end
17
17
 
18
18
  describe 'Resource Owner Password Credentials Flow' do
19
+ let(:client_attributes) { {} }
20
+
19
21
  before do
20
22
  config_is_set(:grant_flows, ["password"])
21
23
  config_is_set(:resource_owner_from_credentials) { User.authenticate! params[:username], params[:password] }
22
- client_exists
24
+ client_exists(client_attributes)
23
25
  create_resource_owner
24
26
  end
25
27
 
26
28
  context 'with valid user credentials' do
27
- it 'should issue new token' do
28
- expect do
29
- post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)
30
- end.to change { Doorkeeper::AccessToken.count }.by(1)
29
+ context "with non-confidential/public client" do
30
+ let(:client_attributes) { { confidential: false } }
31
+
32
+ context "when client_secret absent" do
33
+ it "should issue new token" do
34
+ expect do
35
+ post password_token_endpoint_url(client_id: @client.uid, resource_owner: @resource_owner)
36
+ end.to change { Doorkeeper::AccessToken.count }.by(1)
37
+
38
+ token = Doorkeeper::AccessToken.first
39
+
40
+ expect(token.application_id).to eq @client.id
41
+ should_have_json 'access_token', token.token
42
+ end
43
+ end
44
+
45
+ context "when client_secret present" do
46
+ it "should issue new token" do
47
+ expect do
48
+ post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)
49
+ end.to change { Doorkeeper::AccessToken.count }.by(1)
50
+
51
+ token = Doorkeeper::AccessToken.first
52
+
53
+ expect(token.application_id).to eq @client.id
54
+ should_have_json 'access_token', token.token
55
+ end
56
+
57
+ context "when client_secret incorrect" do
58
+ it "should not issue new token" do
59
+ expect do
60
+ post password_token_endpoint_url(client_id: @client.uid, client_secret: 'foobar', resource_owner: @resource_owner)
61
+ end.not_to(change { Doorkeeper::AccessToken.count })
62
+
63
+ expect(response).not_to be_ok
64
+ end
65
+ end
66
+ end
67
+ end
31
68
 
32
- token = Doorkeeper::AccessToken.first
69
+ context "with confidential/private client" do
70
+ it "should issue new token" do
71
+ expect do
72
+ post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)
73
+ end.to change { Doorkeeper::AccessToken.count }.by(1)
74
+
75
+ token = Doorkeeper::AccessToken.first
33
76
 
34
- should_have_json 'access_token', token.token
77
+ expect(token.application_id).to eq @client.id
78
+ should_have_json 'access_token', token.token
79
+ end
80
+
81
+ context "when client_secret absent" do
82
+ it "should not issue new token" do
83
+ expect do
84
+ post password_token_endpoint_url(client_id: @client.uid, resource_owner: @resource_owner)
85
+ end.not_to(change { Doorkeeper::AccessToken.count })
86
+
87
+ expect(response).not_to be_ok
88
+ end
89
+ end
35
90
  end
36
91
 
37
92
  it 'should issue new token without client credentials' do
38
93
  expect do
39
94
  post password_token_endpoint_url(resource_owner: @resource_owner)
40
- end.to change { Doorkeeper::AccessToken.count }.by(1)
95
+ end.to(change { Doorkeeper::AccessToken.count }.by(1))
41
96
 
42
97
  token = Doorkeeper::AccessToken.first
43
98
 
44
- should_have_json 'access_token', token.token
99
+ expect(token.application_id).to be_nil
100
+ should_have_json 'access_token', token.token
45
101
  end
46
102
 
47
103
  it 'should issue a refresh token if enabled' do
@@ -51,7 +107,7 @@ describe 'Resource Owner Password Credentials Flow' do
51
107
 
52
108
  token = Doorkeeper::AccessToken.first
53
109
 
54
- should_have_json 'refresh_token', token.refresh_token
110
+ should_have_json 'refresh_token', token.refresh_token
55
111
  end
56
112
 
57
113
  it 'should return the same token if it is still accessible' do
@@ -64,6 +120,45 @@ describe 'Resource Owner Password Credentials Flow' do
64
120
  expect(Doorkeeper::AccessToken.count).to be(1)
65
121
  should_have_json 'access_token', Doorkeeper::AccessToken.first.token
66
122
  end
123
+
124
+ context 'with valid, default scope' do
125
+ before do
126
+ default_scopes_exist :public
127
+ end
128
+
129
+ it 'should issue new token' do
130
+ expect do
131
+ post password_token_endpoint_url(client: @client, resource_owner: @resource_owner, scope: 'public')
132
+ end.to change { Doorkeeper::AccessToken.count }.by(1)
133
+
134
+ token = Doorkeeper::AccessToken.first
135
+
136
+ expect(token.application_id).to eq @client.id
137
+ should_have_json 'access_token', token.token
138
+ should_have_json 'scope', 'public'
139
+ end
140
+ end
141
+ end
142
+
143
+ context 'with invalid scopes' do
144
+ subject do
145
+ post password_token_endpoint_url(client: @client,
146
+ resource_owner: @resource_owner,
147
+ scope: 'random')
148
+ end
149
+
150
+ it 'should not issue new token' do
151
+ expect { subject }.to_not(change { Doorkeeper::AccessToken.count })
152
+ end
153
+
154
+ it 'should return invalid_scope error' do
155
+ subject
156
+ should_have_json 'error', 'invalid_scope'
157
+ should_have_json 'error_description', translated_error_message(:invalid_scope)
158
+ should_not_have_json 'access_token'
159
+
160
+ expect(response.status).to eq(401)
161
+ end
67
162
  end
68
163
 
69
164
  context 'with invalid user credentials' do
@@ -72,23 +167,31 @@ describe 'Resource Owner Password Credentials Flow' do
72
167
  post password_token_endpoint_url(client: @client,
73
168
  resource_owner_username: @resource_owner.name,
74
169
  resource_owner_password: 'wrongpassword')
75
- end.to_not change { Doorkeeper::AccessToken.count }
170
+ end.to_not(change { Doorkeeper::AccessToken.count })
76
171
  end
77
172
 
78
173
  it 'should not issue new token without credentials' do
79
174
  expect do
80
175
  post password_token_endpoint_url(client: @client)
81
- end.to_not change { Doorkeeper::AccessToken.count }
176
+ end.to_not(change { Doorkeeper::AccessToken.count })
82
177
  end
83
178
  end
84
179
 
85
- context 'with invalid client credentials' do
180
+ context 'with invalid confidential client credentials' do
86
181
  it 'should not issue new token with bad client credentials' do
87
182
  expect do
88
183
  post password_token_endpoint_url(client_id: @client.uid,
89
184
  client_secret: 'bad_secret',
90
185
  resource_owner: @resource_owner)
91
- end.to_not change { Doorkeeper::AccessToken.count }
186
+ end.to_not(change { Doorkeeper::AccessToken.count })
187
+ end
188
+ end
189
+
190
+ context 'with invalid public client id' do
191
+ it 'should not issue new token with bad client id' do
192
+ expect do
193
+ post password_token_endpoint_url(client_id: 'bad_id', resource_owner: @resource_owner)
194
+ end.to_not(change { Doorkeeper::AccessToken.count })
92
195
  end
93
196
  end
94
197
  end
@@ -37,20 +37,62 @@ describe 'Refresh Token Flow' do
37
37
 
38
38
  context 'refreshing the token' do
39
39
  before do
40
- @token = FactoryGirl.create(:access_token, application: @client, resource_owner_id: 1, use_refresh_token: true)
40
+ @token = FactoryBot.create(
41
+ :access_token,
42
+ application: @client,
43
+ resource_owner_id: 1,
44
+ use_refresh_token: true
45
+ )
41
46
  end
42
47
 
43
- it 'client request a token with refresh token' do
44
- post refresh_token_endpoint_url(client: @client, refresh_token: @token.refresh_token)
45
- should_have_json 'refresh_token', Doorkeeper::AccessToken.last.refresh_token
46
- expect(@token.reload).to be_revoked
48
+ context "refresh_token revoked on use" do
49
+ it 'client request a token with refresh token' do
50
+ post refresh_token_endpoint_url(
51
+ client: @client, refresh_token: @token.refresh_token
52
+ )
53
+ should_have_json(
54
+ 'refresh_token', Doorkeeper::AccessToken.last.refresh_token
55
+ )
56
+ expect(@token.reload).not_to be_revoked
57
+ end
58
+
59
+ it 'client request a token with expired access token' do
60
+ @token.update_attribute :expires_in, -100
61
+ post refresh_token_endpoint_url(
62
+ client: @client, refresh_token: @token.refresh_token
63
+ )
64
+ should_have_json(
65
+ 'refresh_token', Doorkeeper::AccessToken.last.refresh_token
66
+ )
67
+ expect(@token.reload).not_to be_revoked
68
+ end
47
69
  end
48
70
 
49
- it 'client request a token with expired access token' do
50
- @token.update_attribute :expires_in, -100
51
- post refresh_token_endpoint_url(client: @client, refresh_token: @token.refresh_token)
52
- should_have_json 'refresh_token', Doorkeeper::AccessToken.last.refresh_token
53
- expect(@token.reload).to be_revoked
71
+ context "refresh_token revoked on refresh_token request" do
72
+ before do
73
+ allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
74
+ end
75
+
76
+ it 'client request a token with refresh token' do
77
+ post refresh_token_endpoint_url(
78
+ client: @client, refresh_token: @token.refresh_token
79
+ )
80
+ should_have_json(
81
+ 'refresh_token', Doorkeeper::AccessToken.last.refresh_token
82
+ )
83
+ expect(@token.reload).to be_revoked
84
+ end
85
+
86
+ it 'client request a token with expired access token' do
87
+ @token.update_attribute :expires_in, -100
88
+ post refresh_token_endpoint_url(
89
+ client: @client, refresh_token: @token.refresh_token
90
+ )
91
+ should_have_json(
92
+ 'refresh_token', Doorkeeper::AccessToken.last.refresh_token
93
+ )
94
+ expect(@token.reload).to be_revoked
95
+ end
54
96
  end
55
97
 
56
98
  it 'client gets an error for invalid refresh token' do
@@ -59,14 +101,14 @@ describe 'Refresh Token Flow' do
59
101
  should_have_json 'error', 'invalid_grant'
60
102
  end
61
103
 
62
- it 'client gets an error for revoked acccess token' do
104
+ it 'client gets an error for revoked access token' do
63
105
  @token.revoke
64
106
  post refresh_token_endpoint_url(client: @client, refresh_token: @token.refresh_token)
65
107
  should_not_have_json 'refresh_token'
66
108
  should_have_json 'error', 'invalid_grant'
67
109
  end
68
110
 
69
- it 'second of simultaneous client requests get an error for revoked acccess token' do
111
+ it 'second of simultaneous client requests get an error for revoked access token' do
70
112
  allow_any_instance_of(Doorkeeper::AccessToken).to receive(:revoked?).and_return(false, true)
71
113
  post refresh_token_endpoint_url(client: @client, refresh_token: @token.refresh_token)
72
114
 
@@ -79,20 +121,48 @@ describe 'Refresh Token Flow' do
79
121
  before do
80
122
  # enable password auth to simulate other devices
81
123
  config_is_set(:grant_flows, ["password"])
82
- config_is_set(:resource_owner_from_credentials) { User.authenticate! params[:username], params[:password] }
124
+ config_is_set(:resource_owner_from_credentials) do
125
+ User.authenticate! params[:username], params[:password]
126
+ end
83
127
  create_resource_owner
84
- _another_token = post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)
128
+ _another_token = post password_token_endpoint_url(
129
+ client: @client, resource_owner: @resource_owner
130
+ )
85
131
  last_token.update_attribute :created_at, 5.seconds.ago
86
132
 
87
- @token = FactoryGirl.create(:access_token, application: @client, resource_owner_id: @resource_owner.id, use_refresh_token: true)
133
+ @token = FactoryBot.create(
134
+ :access_token,
135
+ application: @client,
136
+ resource_owner_id: @resource_owner.id,
137
+ use_refresh_token: true
138
+ )
88
139
  @token.update_attribute :expires_in, -100
89
140
  end
90
141
 
91
- it 'client request a token after creating another token with the same user' do
92
- post refresh_token_endpoint_url(client: @client, refresh_token: @token.refresh_token)
142
+ context "refresh_token revoked on use" do
143
+ it 'client request a token after creating another token with the same user' do
144
+ post refresh_token_endpoint_url(
145
+ client: @client, refresh_token: @token.refresh_token
146
+ )
147
+
148
+ should_have_json 'refresh_token', last_token.refresh_token
149
+ expect(@token.reload).not_to be_revoked
150
+ end
151
+ end
152
+
153
+ context "refresh_token revoked on refresh_token request" do
154
+ before do
155
+ allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
156
+ end
157
+
158
+ it 'client request a token after creating another token with the same user' do
159
+ post refresh_token_endpoint_url(
160
+ client: @client, refresh_token: @token.refresh_token
161
+ )
93
162
 
94
- should_have_json 'refresh_token', last_token.refresh_token
95
- expect(@token.reload).to be_revoked
163
+ should_have_json 'refresh_token', last_token.refresh_token
164
+ expect(@token.reload).to be_revoked
165
+ end
96
166
  end
97
167
 
98
168
  def last_token