doorkeeper 0.3.0 → 0.4.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 (176) hide show
  1. data/.gitignore +12 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +8 -0
  4. data/CHANGELOG.md +98 -0
  5. data/Gemfile +6 -0
  6. data/README.md +42 -6
  7. data/app/controllers/doorkeeper/application_controller.rb +4 -11
  8. data/app/controllers/doorkeeper/applications_controller.rb +7 -1
  9. data/app/controllers/doorkeeper/authorizations_controller.rb +11 -1
  10. data/app/controllers/doorkeeper/authorized_applications_controller.rb +3 -3
  11. data/app/controllers/doorkeeper/tokens_controller.rb +19 -5
  12. data/app/models/doorkeeper/access_grant.rb +28 -0
  13. data/app/models/doorkeeper/access_token.rb +65 -0
  14. data/app/models/doorkeeper/application.rb +54 -0
  15. data/app/views/doorkeeper/applications/index.html.erb +1 -1
  16. data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
  17. data/app/views/doorkeeper/authorizations/new.html.erb +15 -15
  18. data/app/views/doorkeeper/authorized_applications/index.html.erb +1 -2
  19. data/config/locales/en.yml +3 -0
  20. data/doorkeeper.gemspec +28 -0
  21. data/gemfiles/gemfile.rails-3.1.x +7 -0
  22. data/gemfiles/gemfile.rails-3.2.x +7 -0
  23. data/lib/doorkeeper/config.rb +73 -12
  24. data/lib/doorkeeper/doorkeeper_for.rb +19 -15
  25. data/lib/doorkeeper/engine.rb +28 -0
  26. data/lib/doorkeeper/models/scopes.rb +13 -0
  27. data/lib/doorkeeper/oauth/access_token_request.rb +9 -20
  28. data/lib/doorkeeper/oauth/authorization/code.rb +1 -1
  29. data/lib/doorkeeper/oauth/authorization/token.rb +2 -2
  30. data/lib/doorkeeper/oauth/authorization_request.rb +18 -23
  31. data/lib/doorkeeper/oauth/client/credentials.rb +21 -0
  32. data/lib/doorkeeper/oauth/client/methods.rb +18 -0
  33. data/lib/doorkeeper/oauth/client.rb +27 -0
  34. data/lib/doorkeeper/oauth/client_credentials/creator.rb +29 -0
  35. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +35 -0
  36. data/lib/doorkeeper/oauth/client_credentials/response.rb +42 -0
  37. data/lib/doorkeeper/oauth/client_credentials/validation.rb +33 -0
  38. data/lib/doorkeeper/oauth/client_credentials_request.rb +46 -0
  39. data/lib/doorkeeper/oauth/error.rb +9 -0
  40. data/lib/doorkeeper/oauth/error_response.rb +30 -0
  41. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +2 -2
  42. data/lib/doorkeeper/oauth/password_access_token_request.rb +130 -0
  43. data/lib/doorkeeper/oauth/scopes.rb +60 -0
  44. data/lib/doorkeeper/version.rb +1 -1
  45. data/lib/doorkeeper.rb +23 -3
  46. data/lib/generators/doorkeeper/install_generator.rb +1 -0
  47. data/lib/generators/doorkeeper/templates/initializer.rb +12 -7
  48. data/lib/generators/doorkeeper/templates/migration.rb +1 -1
  49. data/lib/generators/doorkeeper/views_generator.rb +15 -0
  50. data/script/rails +6 -0
  51. data/script/run_all +11 -0
  52. data/spec/controllers/applications_controller_spec.rb +18 -0
  53. data/spec/controllers/authorizations_controller_spec.rb +131 -0
  54. data/spec/controllers/protected_resources_controller_spec.rb +308 -0
  55. data/spec/controllers/tokens_controller_spec.rb +34 -0
  56. data/spec/dummy/Rakefile +7 -0
  57. data/spec/dummy/app/assets/javascripts/application.js +9 -0
  58. data/spec/dummy/app/assets/stylesheets/application.css +7 -0
  59. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  60. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +12 -0
  61. data/spec/dummy/app/controllers/home_controller.rb +17 -0
  62. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +11 -0
  63. data/spec/dummy/app/helpers/application_helper.rb +5 -0
  64. data/spec/dummy/app/models/user.rb +11 -0
  65. data/spec/dummy/app/views/home/index.html.erb +0 -0
  66. data/spec/dummy/app/views/layouts/application.html.erb +16 -0
  67. data/spec/dummy/config/application.rb +53 -0
  68. data/spec/dummy/config/boot.rb +10 -0
  69. data/spec/dummy/config/database.yml +15 -0
  70. data/spec/dummy/config/environment.rb +5 -0
  71. data/spec/dummy/config/environments/development.rb +30 -0
  72. data/spec/dummy/config/environments/production.rb +60 -0
  73. data/spec/dummy/config/environments/test.rb +39 -0
  74. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  75. data/spec/dummy/config/initializers/doorkeeper.rb +41 -0
  76. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  77. data/spec/dummy/config/initializers/session_store.rb +8 -0
  78. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  79. data/spec/dummy/config/locales/doorkeeper.en.yml +5 -0
  80. data/spec/dummy/config/routes.rb +8 -0
  81. data/spec/dummy/config.ru +4 -0
  82. data/spec/dummy/db/migrate/20111122132257_create_users.rb +9 -0
  83. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +5 -0
  84. data/spec/dummy/db/migrate/20120524202412_create_doorkeeper_tables.rb +42 -0
  85. data/spec/dummy/db/schema.rb +62 -0
  86. data/spec/dummy/public/404.html +26 -0
  87. data/spec/dummy/public/422.html +26 -0
  88. data/spec/dummy/public/500.html +26 -0
  89. data/spec/dummy/public/favicon.ico +0 -0
  90. data/spec/dummy/script/rails +6 -0
  91. data/spec/factories/access_grant.rb +9 -0
  92. data/spec/factories/access_token.rb +7 -0
  93. data/spec/factories/application.rb +6 -0
  94. data/spec/generators/install_generator_spec.rb +35 -0
  95. data/spec/generators/templates/routes.rb +3 -0
  96. data/spec/generators/views_generator_spec.rb +27 -0
  97. data/spec/lib/config_spec.rb +87 -0
  98. data/spec/lib/models/expirable_spec.rb +49 -0
  99. data/spec/lib/models/revocable_spec.rb +31 -0
  100. data/spec/lib/models/scopes_spec.rb +32 -0
  101. data/spec/lib/oauth/access_token_request_spec.rb +240 -0
  102. data/spec/lib/oauth/authorization/uri_builder_spec.rb +37 -0
  103. data/spec/lib/oauth/authorization_request_spec.rb +286 -0
  104. data/spec/lib/oauth/client/credentials_spec.rb +47 -0
  105. data/spec/lib/oauth/client/methods_spec.rb +54 -0
  106. data/spec/lib/oauth/client_credentials/creator_spec.rb +47 -0
  107. data/spec/lib/oauth/client_credentials/issuer_spec.rb +57 -0
  108. data/spec/lib/oauth/client_credentials/response_spec.rb +58 -0
  109. data/spec/lib/oauth/client_credentials/validation_spec.rb +29 -0
  110. data/spec/lib/oauth/client_credentials_integration_spec.rb +27 -0
  111. data/spec/lib/oauth/client_credentials_request_spec.rb +60 -0
  112. data/spec/lib/oauth/client_spec.rb +42 -0
  113. data/spec/lib/oauth/error_response_spec.rb +40 -0
  114. data/spec/lib/oauth/error_spec.rb +19 -0
  115. data/spec/lib/oauth/helpers/scope_checker_spec.rb +74 -0
  116. data/spec/lib/oauth/helpers/unique_token_spec.rb +38 -0
  117. data/spec/lib/oauth/helpers/uri_checker_spec.rb +64 -0
  118. data/spec/lib/oauth/password_access_token_request_spec.rb +152 -0
  119. data/spec/lib/oauth/scopes_spec.rb +115 -0
  120. data/spec/models/doorkeeper/access_grant_spec.rb +36 -0
  121. data/spec/models/doorkeeper/access_token_spec.rb +113 -0
  122. data/spec/models/doorkeeper/application_spec.rb +129 -0
  123. data/spec/requests/applications/applications_request_spec.rb +92 -0
  124. data/spec/requests/applications/authorized_applications_spec.rb +30 -0
  125. data/spec/requests/endpoints/authorization_spec.rb +63 -0
  126. data/spec/requests/endpoints/token_spec.rb +29 -0
  127. data/spec/requests/flows/authorization_code_errors_spec.rb +111 -0
  128. data/spec/requests/flows/authorization_code_spec.rb +125 -0
  129. data/spec/requests/flows/client_credentials_spec.rb +56 -0
  130. data/spec/requests/flows/implicit_grant_errors_spec.rb +31 -0
  131. data/spec/requests/flows/implicit_grant_spec.rb +19 -0
  132. data/spec/requests/flows/password_spec.rb +52 -0
  133. data/spec/requests/flows/refresh_token_spec.rb +66 -0
  134. data/spec/requests/flows/skip_authorization_spec.rb +40 -0
  135. data/spec/requests/protected_resources/private_api_spec.rb +43 -0
  136. data/spec/spec_helper.rb +1 -0
  137. data/spec/spec_helper_integration.rb +28 -0
  138. data/spec/support/dependencies/database_cleaner.rb +16 -0
  139. data/spec/support/dependencies/factory_girl.rb +5 -0
  140. data/spec/support/helpers/access_token_request_helper.rb +11 -0
  141. data/spec/support/helpers/authorization_request_helper.rb +32 -0
  142. data/spec/support/helpers/config_helper.rb +9 -0
  143. data/spec/support/helpers/model_helper.rb +45 -0
  144. data/spec/support/helpers/request_spec_helper.rb +72 -0
  145. data/spec/support/helpers/url_helper.rb +51 -0
  146. data/spec/support/shared/controllers_shared_context.rb +60 -0
  147. data/spec/support/shared/models_shared_examples.rb +44 -0
  148. metadata +163 -57
  149. data/app/controllers/doorkeeper/application_controller.rbc +0 -675
  150. data/app/controllers/doorkeeper/applications_controller.rbc +0 -954
  151. data/app/controllers/doorkeeper/authorizations_controller.rbc +0 -659
  152. data/app/controllers/doorkeeper/authorized_applications_controller.rbc +0 -393
  153. data/app/controllers/doorkeeper/tokens_controller.rbc +0 -423
  154. data/app/helpers/doorkeeper/application_helper.rbc +0 -127
  155. data/app/models/access_grant.rb +0 -31
  156. data/app/models/access_grant.rbc +0 -821
  157. data/app/models/access_token.rb +0 -70
  158. data/app/models/access_token.rbc +0 -979
  159. data/app/models/application.rb +0 -45
  160. data/app/models/application.rbc +0 -753
  161. data/config/initializers/form_errors.rbc +0 -272
  162. data/config/routes.rbc +0 -366
  163. data/lib/doorkeeper/config/scope.rb +0 -11
  164. data/lib/doorkeeper/config/scopes.rb +0 -61
  165. data/lib/doorkeeper/config/scopes_builder.rb +0 -18
  166. data/lib/doorkeeper/config.rbc +0 -1385
  167. data/lib/doorkeeper/doorkeeper_for.rbc +0 -1029
  168. data/lib/doorkeeper/engine.rbc +0 -318
  169. data/lib/doorkeeper/oauth/access_token_request.rbc +0 -2042
  170. data/lib/doorkeeper/oauth/authorization_request.rbc +0 -2272
  171. data/lib/doorkeeper/oauth/random_string.rbc +0 -437
  172. data/lib/doorkeeper/validations.rbc +0 -774
  173. data/lib/doorkeeper/version.rbc +0 -130
  174. data/lib/doorkeeper.rbc +0 -407
  175. data/lib/generators/doorkeeper/install_generator.rbc +0 -453
  176. data/lib/tasks/doorkeeper_tasks.rake.compiled.rbc +0 -40
@@ -0,0 +1,63 @@
1
+ require 'spec_helper_integration'
2
+
3
+ feature 'Authorization endpoint' do
4
+ background do
5
+ config_is_set(:authenticate_resource_owner) { User.first || redirect_to('/sign_in') }
6
+ client_exists(:name => "MyApp")
7
+ end
8
+
9
+ scenario 'requires resource owner to be authenticated' do
10
+ visit authorization_endpoint_url(:client => @client)
11
+ i_should_see "Sign in"
12
+ i_should_be_on "/"
13
+ end
14
+
15
+ context 'with authenticated resource owner' do
16
+ background do
17
+ create_resource_owner
18
+ sign_in
19
+ end
20
+
21
+ scenario 'displays the authorization form' do
22
+ visit authorization_endpoint_url(:client => @client)
23
+ i_should_see "Authorize MyApp to use your account?"
24
+ end
25
+
26
+ scenario 'accepts "code" response type' do
27
+ visit authorization_endpoint_url(:client => @client, :response_type => "code")
28
+ i_should_see "Authorize"
29
+ end
30
+
31
+ scenario 'accepts "token" response type' do
32
+ visit authorization_endpoint_url(:client => @client, :response_type => "token")
33
+ i_should_see "Authorize"
34
+ end
35
+ end
36
+
37
+ context 'with scopes' do
38
+ background do
39
+ create_resource_owner
40
+ sign_in
41
+ default_scopes_exist :public
42
+ optional_scopes_exist :write
43
+ end
44
+
45
+ scenario "displays default scopes when no scope was requested" do
46
+ visit authorization_endpoint_url(:client => @client)
47
+ i_should_see "Access your public data"
48
+ i_should_not_see "Update your data"
49
+ end
50
+
51
+ scenario "displays all requested scopes" do
52
+ visit authorization_endpoint_url(:client => @client, :scope => "public write")
53
+ i_should_see "Access your public data"
54
+ i_should_see "Update your data"
55
+ end
56
+
57
+ scenario "does not display default scope if it was not requested" do
58
+ visit authorization_endpoint_url(:client => @client, :scope => "write")
59
+ i_should_not_see "Access your public data"
60
+ i_should_see "Update your data"
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper_integration'
2
+
3
+ feature 'Token endpoint' do
4
+ background do
5
+ client_exists
6
+ authorization_code_exists :application => @client, :scopes => "public"
7
+ end
8
+
9
+ scenario 'respond with correct headers' do
10
+ post token_endpoint_url(:code => @authorization.token, :client => @client)
11
+ should_have_header 'Pragma', 'no-cache'
12
+ should_have_header 'Cache-Control', 'no-store'
13
+ end
14
+
15
+ scenario 'accepts client credentials with basic auth header' do
16
+ post token_endpoint_url(:code => @authorization.token, :redirect_uri => @client.redirect_uri),
17
+ {} ,
18
+ { 'HTTP_AUTHORIZATION' => basic_auth_header_for_client(@client) }
19
+
20
+ should_have_json 'access_token', Doorkeeper::AccessToken.first.token
21
+ end
22
+
23
+ scenario 'returns null for expires_in when a permanent token is set' do
24
+ config_is_set(:access_token_expires_in, nil)
25
+ post token_endpoint_url(:code => @authorization.token, :client => @client)
26
+ should_have_json 'access_token', Doorkeeper::AccessToken.first.token
27
+ should_have_json 'expires_in', nil
28
+ end
29
+ end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper_integration'
2
+
3
+ feature 'Authorization Code Flow Errors' do
4
+ background do
5
+ config_is_set(:authenticate_resource_owner) { User.first || redirect_to('/sign_in') }
6
+ client_exists
7
+ create_resource_owner
8
+ sign_in
9
+ end
10
+
11
+ after do
12
+ access_grant_should_not_exist
13
+ end
14
+
15
+ scenario "redirects with :invalid_request error when :response_type is missing" do
16
+ visit authorization_endpoint_url(:client => @client, :response_type => "")
17
+ i_should_be_on_client_callback @client
18
+ url_should_have_param "error", "invalid_request"
19
+ url_should_have_param "error_description", translated_error_message(:invalid_request)
20
+ end
21
+
22
+ scenario "redirects with :unsupported_response_type error for invalid :response_type" do
23
+ visit authorization_endpoint_url(:client => @client, :response_type => "invalid")
24
+ i_should_be_on_client_callback @client
25
+ url_should_have_param "error", "unsupported_response_type"
26
+ url_should_have_param "error_description", translated_error_message(:unsupported_response_type)
27
+ end
28
+
29
+ [
30
+ [:client_id, :invalid_client],
31
+ [:redirect_uri, :invalid_redirect_uri],
32
+ ].each do |error|
33
+ scenario "displays #{error.last.inspect} error for invalid #{error.first.inspect}" do
34
+ visit authorization_endpoint_url(:client => @client, error.first => "invalid")
35
+ i_should_not_see "Authorize"
36
+ i_should_see_translated_error_message error.last
37
+ end
38
+
39
+ scenario "displays #{error.last.inspect} error when #{error.first.inspect} is missing" do
40
+ visit authorization_endpoint_url(:client => @client, error.first => "")
41
+ i_should_not_see "Authorize"
42
+ i_should_see_translated_error_message error.last
43
+ end
44
+ end
45
+
46
+ context 'when access was denied' do
47
+ scenario 'redirects with error' do
48
+ visit authorization_endpoint_url(:client => @client)
49
+ click_on "Deny"
50
+
51
+ i_should_be_on_client_callback @client
52
+ url_should_not_have_param "code"
53
+ url_should_have_param "error", "access_denied"
54
+ url_should_have_param "error_description", translated_error_message(:access_denied)
55
+ end
56
+
57
+ scenario 'redirects with state parameter' do
58
+ visit authorization_endpoint_url(:client => @client, :state => "return-this")
59
+ click_on "Deny"
60
+
61
+ i_should_be_on_client_callback @client
62
+ url_should_not_have_param "code"
63
+ url_should_have_param "state", "return-this"
64
+ end
65
+ end
66
+
67
+ context 'with scopes' do
68
+ background do
69
+ optional_scopes_exist :write
70
+ end
71
+
72
+ scenario "redirects with :invalid_scope error when scope does not exists" do
73
+ visit authorization_endpoint_url(:client => @client, :scope => "invalid")
74
+
75
+ i_should_be_on_client_callback @client
76
+ url_should_have_param "error", "invalid_scope"
77
+ url_should_have_param "error_description", translated_error_message(:invalid_scope)
78
+ end
79
+ end
80
+ end
81
+
82
+ feature 'Authorization Code Flow Errors', 'after authorization' do
83
+ background do
84
+ client_exists
85
+ authorization_code_exists :application => @client
86
+ end
87
+
88
+ scenario "returns :invalid_grant error when posting an already revoked grant code" do
89
+ # First successful request
90
+ post token_endpoint_url(:code => @authorization.token, :client => @client)
91
+
92
+ # Second attempt with same token
93
+ expect {
94
+ post token_endpoint_url(:code => @authorization.token, :client => @client)
95
+ }.to_not change { Doorkeeper::AccessToken.count }
96
+
97
+ should_not_have_json 'access_token'
98
+ should_have_json 'error', 'invalid_grant'
99
+ should_have_json 'error_description', translated_error_message('invalid_grant')
100
+ end
101
+
102
+ scenario "returns :invalid_grant error for invalid grant code" do
103
+ post token_endpoint_url(:code => "invalid", :client => @client)
104
+
105
+ access_token_should_not_exist
106
+
107
+ should_not_have_json 'access_token'
108
+ should_have_json 'error', 'invalid_grant'
109
+ should_have_json 'error_description', translated_error_message('invalid_grant')
110
+ end
111
+ end
@@ -0,0 +1,125 @@
1
+ require 'spec_helper_integration'
2
+
3
+ feature 'Authorization Code Flow' do
4
+ background do
5
+ config_is_set(:authenticate_resource_owner) { User.first || redirect_to('/sign_in') }
6
+ client_exists
7
+ create_resource_owner
8
+ sign_in
9
+ end
10
+
11
+ scenario 'resource owner authorizes the client' do
12
+ visit authorization_endpoint_url(:client => @client)
13
+ click_on "Authorize"
14
+
15
+ access_grant_should_exist_for(@client, @resource_owner)
16
+
17
+ i_should_be_on_client_callback(@client)
18
+
19
+ url_should_have_param("code", Doorkeeper::AccessGrant.first.token)
20
+ url_should_not_have_param("state")
21
+ url_should_not_have_param("error")
22
+ end
23
+
24
+ scenario 'resource owner authorizes the client with state parameter set' do
25
+ visit authorization_endpoint_url(:client => @client, :state => "return-me")
26
+ click_on "Authorize"
27
+ url_should_have_param("code", Doorkeeper::AccessGrant.first.token)
28
+ url_should_have_param("state", "return-me")
29
+ end
30
+
31
+ scenario 'returns the same token if it is still accessible' do
32
+ client_is_authorized(@client, @resource_owner)
33
+ visit authorization_endpoint_url(:client => @client)
34
+
35
+ authorization_code = Doorkeeper::AccessGrant.first.token
36
+ post token_endpoint_url(:code => authorization_code, :client => @client)
37
+
38
+ Doorkeeper::AccessToken.count.should be(1)
39
+
40
+ should_have_json 'access_token', Doorkeeper::AccessToken.first.token
41
+ end
42
+
43
+ scenario 'revokes and return new token if it is has expired' do
44
+ client_is_authorized(@client, @resource_owner)
45
+ token = Doorkeeper::AccessToken.first
46
+ token.update_attribute :expires_in, -100
47
+ visit authorization_endpoint_url(:client => @client)
48
+
49
+ authorization_code = Doorkeeper::AccessGrant.first.token
50
+ post token_endpoint_url(:code => authorization_code, :client => @client)
51
+
52
+ token.reload.should be_revoked
53
+ Doorkeeper::AccessToken.count.should be(2)
54
+
55
+ should_have_json 'access_token', Doorkeeper::AccessToken.last.token
56
+ end
57
+
58
+ scenario 'resource owner requests an access token with authorization code' do
59
+ visit authorization_endpoint_url(:client => @client)
60
+ click_on "Authorize"
61
+
62
+ authorization_code = Doorkeeper::AccessGrant.first.token
63
+ post token_endpoint_url(:code => authorization_code, :client => @client)
64
+
65
+ access_token_should_exist_for(@client, @resource_owner)
66
+
67
+ should_not_have_json 'error'
68
+
69
+ should_have_json 'access_token', Doorkeeper::AccessToken.first.token
70
+ should_have_json 'token_type', "bearer"
71
+ should_have_json 'expires_in', Doorkeeper::AccessToken.first.expires_in
72
+
73
+ should_not_have_json 'refresh_token'
74
+ end
75
+
76
+ context 'with scopes' do
77
+ background do
78
+ default_scopes_exist :public
79
+ optional_scopes_exist :write
80
+ end
81
+
82
+ scenario 'resource owner authorizes the client with default scopes' do
83
+ visit authorization_endpoint_url(:client => @client)
84
+ click_on "Authorize"
85
+ access_grant_should_exist_for(@client, @resource_owner)
86
+ access_grant_should_have_scopes :public
87
+ end
88
+
89
+ scenario 'resource owner authorizes the client with required scopes' do
90
+ visit authorization_endpoint_url(:client => @client, :scope => "public write")
91
+ click_on "Authorize"
92
+ access_grant_should_have_scopes :public, :write
93
+ end
94
+
95
+ scenario 'resource owner authorizes the client with required scopes (without defaults)' do
96
+ visit authorization_endpoint_url(:client => @client, :scope => "write")
97
+ click_on "Authorize"
98
+ access_grant_should_have_scopes :write
99
+ end
100
+
101
+ scenario 'new access token matches required scopes' do
102
+ visit authorization_endpoint_url(:client => @client, :scope => "public write")
103
+ click_on "Authorize"
104
+
105
+ authorization_code = Doorkeeper::AccessGrant.first.token
106
+ post token_endpoint_url(:code => authorization_code, :client => @client)
107
+
108
+ access_token_should_exist_for(@client, @resource_owner)
109
+ access_token_should_have_scopes :public, :write
110
+ end
111
+
112
+ scenario 'returns new token if scopes have changed' do
113
+ client_is_authorized(@client, @resource_owner, :scopes => "public write")
114
+ visit authorization_endpoint_url(:client => @client, :scope => "public")
115
+ click_on "Authorize"
116
+
117
+ authorization_code = Doorkeeper::AccessGrant.first.token
118
+ post token_endpoint_url(:code => authorization_code, :client => @client)
119
+
120
+ Doorkeeper::AccessToken.count.should be(2)
121
+
122
+ should_have_json 'access_token', Doorkeeper::AccessToken.last.token
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper_integration'
2
+
3
+ describe 'Client Credentials Request' do
4
+ let(:client) { FactoryGirl.create :application }
5
+
6
+ context 'a valid request' do
7
+ it 'authorizes the client and returns the token response' do
8
+ headers = authorization client.uid, client.secret
9
+ params = { :grant_type => 'client_credentials' }
10
+
11
+ post '/oauth/token', params, headers
12
+
13
+ should_have_json 'access_token', Doorkeeper::AccessToken.first.token
14
+ should_have_json 'expires_in', Doorkeeper.configuration.access_token_expires_in
15
+ should_have_json 'scope', ''
16
+
17
+ should_not_have_json 'error'
18
+ should_not_have_json 'error_description'
19
+ should_not_have_json 'refresh_token'
20
+ end
21
+
22
+ context 'with scopes' do
23
+ before do
24
+ optional_scopes_exist :write
25
+ end
26
+
27
+ it 'adds the scope to the token an returns in the response' do
28
+ headers = authorization client.uid, client.secret
29
+ params = { :grant_type => 'client_credentials', :scope => 'write' }
30
+
31
+ post '/oauth/token', params, headers
32
+
33
+ should_have_json 'access_token', Doorkeeper::AccessToken.first.token
34
+ should_have_json 'scope', 'write'
35
+ end
36
+ end
37
+ end
38
+
39
+ context 'an invalid request' do
40
+ it 'does not authorize the client and returns the error' do
41
+ headers = {}
42
+ params = { :grant_type => 'client_credentials' }
43
+
44
+ post '/oauth/token', params, headers
45
+
46
+ should_have_json 'error', 'invalid_client'
47
+ should_have_json 'error_description', translated_error_message(:invalid_client)
48
+ should_not_have_json 'access_token'
49
+ end
50
+ end
51
+
52
+ def authorization(username, password)
53
+ credentials = ActionController::HttpAuthentication::Basic.encode_credentials username, password
54
+ { 'HTTP_AUTHORIZATION' => credentials }
55
+ end
56
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper_integration'
2
+
3
+ feature 'Implicit Grant Flow Errors' do
4
+ background do
5
+ config_is_set(:authenticate_resource_owner) { User.first || redirect_to('/sign_in') }
6
+ client_exists
7
+ create_resource_owner
8
+ sign_in
9
+ end
10
+
11
+ after do
12
+ access_token_should_not_exist
13
+ end
14
+
15
+ [
16
+ [:client_id, :invalid_client],
17
+ [:redirect_uri, :invalid_redirect_uri],
18
+ ].each do |error|
19
+ scenario "displays #{error.last.inspect} error for invalid #{error.first.inspect}" do
20
+ visit authorization_endpoint_url(:client => @client, error.first => "invalid", :response_type => "token")
21
+ i_should_not_see "Authorize"
22
+ i_should_see_translated_error_message error.last
23
+ end
24
+
25
+ scenario "displays #{error.last.inspect} error when #{error.first.inspect} is missing" do
26
+ visit authorization_endpoint_url(:client => @client, error.first => "", :response_type => "token")
27
+ i_should_not_see "Authorize"
28
+ i_should_see_translated_error_message error.last
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper_integration'
2
+
3
+ feature 'Implicit Grant Flow' do
4
+ background do
5
+ config_is_set(:authenticate_resource_owner) { User.first || redirect_to('/sign_in') }
6
+ client_exists
7
+ create_resource_owner
8
+ sign_in
9
+ end
10
+
11
+ scenario 'resource owner authorizes the client' do
12
+ visit authorization_endpoint_url(:client => @client, :response_type => 'token')
13
+ click_on "Authorize"
14
+
15
+ access_token_should_exist_for @client, @resource_owner
16
+
17
+ i_should_be_on_client_callback @client
18
+ end
19
+ end
@@ -0,0 +1,52 @@
1
+ # coding: utf-8
2
+
3
+ # TODO: this flow should be configurable (letting Doorkeeper users decide if
4
+ # they want to make it available)
5
+
6
+ require 'spec_helper_integration'
7
+
8
+ feature 'Resource Owner Password Credentials Flow' do
9
+ background do
10
+ config_is_set(:resource_owner_from_credentials) { User.authenticate! params[:username], params[:password] }
11
+ client_exists
12
+ create_resource_owner
13
+ end
14
+
15
+ context 'with valid user credentials' do
16
+ scenario "should issue new token" do
17
+ expect {
18
+ post password_token_endpoint_url(:client => @client, :resource_owner => @resource_owner)
19
+ }.to change { Doorkeeper::AccessToken.count }.by(1)
20
+
21
+ token = Doorkeeper::AccessToken.first
22
+
23
+ should_have_json 'access_token', token.token
24
+ end
25
+
26
+ scenario "should issue a refresh token if enabled" do
27
+ config_is_set(:refresh_token_enabled, true)
28
+
29
+ post password_token_endpoint_url(:client => @client, :resource_owner => @resource_owner)
30
+
31
+ token = Doorkeeper::AccessToken.first
32
+
33
+ should_have_json 'refresh_token', token.refresh_token
34
+ end
35
+ end
36
+
37
+ context "with invalid user credentials" do
38
+ scenario "should not issue new token with bad password" do
39
+ expect {
40
+ post password_token_endpoint_url( :client => @client,
41
+ :resource_owner_username => @resource_owner.name,
42
+ :resource_owner_password => 'wrongpassword')
43
+ }.to_not change { Doorkeeper::AccessToken.count }
44
+ end
45
+
46
+ scenario "should not issue new token without credentials" do
47
+ expect {
48
+ post password_token_endpoint_url( :client => @client)
49
+ }.to_not change { Doorkeeper::AccessToken.count }
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper_integration'
2
+
3
+ feature "Refresh Token Flow" do
4
+ before do
5
+ Doorkeeper.configure { use_refresh_token }
6
+ client_exists
7
+ end
8
+
9
+ context "issuing a refresh token" do
10
+ before do
11
+ authorization_code_exists :application => @client
12
+ end
13
+
14
+ scenario "client gets the refresh token and refreshses it" do
15
+ post token_endpoint_url(:code => @authorization.token, :client => @client)
16
+
17
+ token = Doorkeeper::AccessToken.first
18
+
19
+ should_have_json 'access_token', token.token
20
+ should_have_json 'refresh_token', token.refresh_token
21
+
22
+ @authorization.reload.should be_revoked
23
+
24
+ post refresh_token_endpoint_url(:client => @client, :refresh_token => token.refresh_token)
25
+
26
+ new_token = Doorkeeper::AccessToken.last
27
+ should_have_json 'access_token', new_token.token
28
+ should_have_json 'refresh_token', new_token.refresh_token
29
+
30
+ token.token.should_not == new_token.token
31
+ token.refresh_token.should_not == new_token.refresh_token
32
+ end
33
+ end
34
+
35
+ context "refreshing the token" do
36
+ before do
37
+ @token = FactoryGirl.create(:access_token, :application => @client, :resource_owner_id => 1, :use_refresh_token => true)
38
+ end
39
+
40
+ scenario "client request a token with refresh token" do
41
+ post refresh_token_endpoint_url(:client => @client, :refresh_token => @token.refresh_token)
42
+ should_have_json 'refresh_token', Doorkeeper::AccessToken.last.refresh_token
43
+ @token.reload.should be_revoked
44
+ end
45
+
46
+ scenario "client request a token with expired access token" do
47
+ @token.update_attribute :expires_in, -100
48
+ post refresh_token_endpoint_url(:client => @client, :refresh_token => @token.refresh_token)
49
+ should_have_json 'refresh_token', Doorkeeper::AccessToken.last.refresh_token
50
+ @token.reload.should be_revoked
51
+ end
52
+
53
+ scenario "client gets an error for invalid refresh token" do
54
+ post refresh_token_endpoint_url(:client => @client, :refresh_token => "invalid")
55
+ should_not_have_json 'refresh_token'
56
+ should_have_json 'error', 'invalid_grant'
57
+ end
58
+
59
+ scenario "client gets an error for revoked acccess token" do
60
+ @token.revoke
61
+ post refresh_token_endpoint_url(:client => @client, :refresh_token => @token.refresh_token)
62
+ should_not_have_json 'refresh_token'
63
+ should_have_json 'error', 'invalid_grant'
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper_integration'
2
+
3
+ feature 'Skip authorization form' do
4
+ background do
5
+ config_is_set(:authenticate_resource_owner) { User.first || redirect_to('/sign_in') }
6
+ client_exists
7
+ default_scopes_exist :public
8
+ optional_scopes_exist :write
9
+ end
10
+
11
+ context 'for previously authorized clients' do
12
+ background do
13
+ create_resource_owner
14
+ sign_in
15
+ end
16
+
17
+ scenario 'skips the authorization and return a new grant code' do
18
+ client_is_authorized(@client, @resource_owner, :scopes => "public")
19
+ visit authorization_endpoint_url(:client => @client)
20
+
21
+ i_should_not_see "Authorize"
22
+ client_should_be_authorized @client
23
+ i_should_be_on_client_callback @client
24
+ url_should_have_param "code", Doorkeeper::AccessGrant.first.token
25
+ end
26
+
27
+ scenario 'does not skip authorization when scopes differ' do
28
+ client_is_authorized(@client, @resource_owner, :scopes => "public write")
29
+ visit authorization_endpoint_url(:client => @client, :scope => "public")
30
+ i_should_see "Authorize"
31
+ end
32
+
33
+ scenario 'creates grant with new scope when scopes differ' do
34
+ client_is_authorized(@client, @resource_owner, :scopes => "public write")
35
+ visit authorization_endpoint_url(:client => @client, :scope => "public")
36
+ click_on "Authorize"
37
+ access_grant_should_have_scopes :public
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper_integration'
2
+
3
+ feature 'Private API' do
4
+ background do
5
+ @client = FactoryGirl.create(:application)
6
+ @resource = User.create!(:name => "Joe", :password => "sekret")
7
+ @token = client_is_authorized(@client, @resource)
8
+ end
9
+
10
+ scenario 'client requests protected resource with valid token' do
11
+ with_access_token_header @token.token
12
+ visit '/full_protected_resources'
13
+ page.body.should have_content("index")
14
+ end
15
+
16
+ scenario 'client attempts to request protected resource with invalid token' do
17
+ with_access_token_header "invalid"
18
+ visit '/full_protected_resources'
19
+ response_status_should_be 401
20
+ end
21
+
22
+ scenario 'client attempts to request protected resource with expired token' do
23
+ @token.update_attribute :expires_in, -100 # expires token
24
+ with_access_token_header @token.token
25
+ visit '/full_protected_resources'
26
+ response_status_should_be 401
27
+ end
28
+
29
+ scenario 'client requests protected resource with permanent token' do
30
+ @token.update_attribute :expires_in, nil # never expires
31
+ with_access_token_header @token.token
32
+ visit '/full_protected_resources'
33
+ page.body.should have_content("index")
34
+ end
35
+
36
+ scenario 'access token with no scopes' do
37
+ optional_scopes_exist :admin
38
+ @token.update_attribute :scopes, nil
39
+ with_access_token_header @token.token
40
+ visit '/full_protected_resources/1.json'
41
+ response_status_should_be 401
42
+ end
43
+ end
@@ -0,0 +1 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), "../lib"))
@@ -0,0 +1,28 @@
1
+ ENV["RAILS_ENV"] ||= 'test'
2
+ require File.expand_path("../dummy/config/environment", __FILE__)
3
+
4
+ require 'rspec/rails'
5
+ require 'rspec/autorun'
6
+ require 'generator_spec/test_case'
7
+ require 'timecop'
8
+
9
+ ENGINE_RAILS_ROOT = File.join(File.dirname(__FILE__), '../')
10
+
11
+ puts "====> Rails version: #{Rails.version}"
12
+ puts "====> Ruby version: #{RUBY_VERSION}"
13
+
14
+ # load schema to in memory sqlite
15
+ ActiveRecord::Migration.verbose = false
16
+ load Rails.root + "db/schema.rb"
17
+
18
+ Dir[File.join(ENGINE_RAILS_ROOT, "spec/support/**/*.rb")].each { |f| require f }
19
+
20
+ RSpec.configure do |config|
21
+ config.mock_with :rspec
22
+
23
+ config.infer_base_class_for_anonymous_controllers = false
24
+
25
+ config.before do
26
+ Doorkeeper.configure {}
27
+ end
28
+ end