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.
- data/.gitignore +12 -0
- data/.rspec +1 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +98 -0
- data/Gemfile +6 -0
- data/README.md +42 -6
- data/app/controllers/doorkeeper/application_controller.rb +4 -11
- data/app/controllers/doorkeeper/applications_controller.rb +7 -1
- data/app/controllers/doorkeeper/authorizations_controller.rb +11 -1
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +3 -3
- data/app/controllers/doorkeeper/tokens_controller.rb +19 -5
- data/app/models/doorkeeper/access_grant.rb +28 -0
- data/app/models/doorkeeper/access_token.rb +65 -0
- data/app/models/doorkeeper/application.rb +54 -0
- data/app/views/doorkeeper/applications/index.html.erb +1 -1
- data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
- data/app/views/doorkeeper/authorizations/new.html.erb +15 -15
- data/app/views/doorkeeper/authorized_applications/index.html.erb +1 -2
- data/config/locales/en.yml +3 -0
- data/doorkeeper.gemspec +28 -0
- data/gemfiles/gemfile.rails-3.1.x +7 -0
- data/gemfiles/gemfile.rails-3.2.x +7 -0
- data/lib/doorkeeper/config.rb +73 -12
- data/lib/doorkeeper/doorkeeper_for.rb +19 -15
- data/lib/doorkeeper/engine.rb +28 -0
- data/lib/doorkeeper/models/scopes.rb +13 -0
- data/lib/doorkeeper/oauth/access_token_request.rb +9 -20
- data/lib/doorkeeper/oauth/authorization/code.rb +1 -1
- data/lib/doorkeeper/oauth/authorization/token.rb +2 -2
- data/lib/doorkeeper/oauth/authorization_request.rb +18 -23
- data/lib/doorkeeper/oauth/client/credentials.rb +21 -0
- data/lib/doorkeeper/oauth/client/methods.rb +18 -0
- data/lib/doorkeeper/oauth/client.rb +27 -0
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +29 -0
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +35 -0
- data/lib/doorkeeper/oauth/client_credentials/response.rb +42 -0
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +33 -0
- data/lib/doorkeeper/oauth/client_credentials_request.rb +46 -0
- data/lib/doorkeeper/oauth/error.rb +9 -0
- data/lib/doorkeeper/oauth/error_response.rb +30 -0
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +2 -2
- data/lib/doorkeeper/oauth/password_access_token_request.rb +130 -0
- data/lib/doorkeeper/oauth/scopes.rb +60 -0
- data/lib/doorkeeper/version.rb +1 -1
- data/lib/doorkeeper.rb +23 -3
- data/lib/generators/doorkeeper/install_generator.rb +1 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +12 -7
- data/lib/generators/doorkeeper/templates/migration.rb +1 -1
- data/lib/generators/doorkeeper/views_generator.rb +15 -0
- data/script/rails +6 -0
- data/script/run_all +11 -0
- data/spec/controllers/applications_controller_spec.rb +18 -0
- data/spec/controllers/authorizations_controller_spec.rb +131 -0
- data/spec/controllers/protected_resources_controller_spec.rb +308 -0
- data/spec/controllers/tokens_controller_spec.rb +34 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +9 -0
- data/spec/dummy/app/assets/stylesheets/application.css +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/full_protected_resources_controller.rb +12 -0
- data/spec/dummy/app/controllers/home_controller.rb +17 -0
- data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +11 -0
- data/spec/dummy/app/helpers/application_helper.rb +5 -0
- data/spec/dummy/app/models/user.rb +11 -0
- data/spec/dummy/app/views/home/index.html.erb +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +16 -0
- data/spec/dummy/config/application.rb +53 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +15 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +30 -0
- data/spec/dummy/config/environments/production.rb +60 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/doorkeeper.rb +41 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/doorkeeper.en.yml +5 -0
- data/spec/dummy/config/routes.rb +8 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20111122132257_create_users.rb +9 -0
- data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +5 -0
- data/spec/dummy/db/migrate/20120524202412_create_doorkeeper_tables.rb +42 -0
- data/spec/dummy/db/schema.rb +62 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/factories/access_grant.rb +9 -0
- data/spec/factories/access_token.rb +7 -0
- data/spec/factories/application.rb +6 -0
- data/spec/generators/install_generator_spec.rb +35 -0
- data/spec/generators/templates/routes.rb +3 -0
- data/spec/generators/views_generator_spec.rb +27 -0
- data/spec/lib/config_spec.rb +87 -0
- data/spec/lib/models/expirable_spec.rb +49 -0
- data/spec/lib/models/revocable_spec.rb +31 -0
- data/spec/lib/models/scopes_spec.rb +32 -0
- data/spec/lib/oauth/access_token_request_spec.rb +240 -0
- data/spec/lib/oauth/authorization/uri_builder_spec.rb +37 -0
- data/spec/lib/oauth/authorization_request_spec.rb +286 -0
- data/spec/lib/oauth/client/credentials_spec.rb +47 -0
- data/spec/lib/oauth/client/methods_spec.rb +54 -0
- data/spec/lib/oauth/client_credentials/creator_spec.rb +47 -0
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +57 -0
- data/spec/lib/oauth/client_credentials/response_spec.rb +58 -0
- data/spec/lib/oauth/client_credentials/validation_spec.rb +29 -0
- data/spec/lib/oauth/client_credentials_integration_spec.rb +27 -0
- data/spec/lib/oauth/client_credentials_request_spec.rb +60 -0
- data/spec/lib/oauth/client_spec.rb +42 -0
- data/spec/lib/oauth/error_response_spec.rb +40 -0
- data/spec/lib/oauth/error_spec.rb +19 -0
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +74 -0
- data/spec/lib/oauth/helpers/unique_token_spec.rb +38 -0
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +64 -0
- data/spec/lib/oauth/password_access_token_request_spec.rb +152 -0
- data/spec/lib/oauth/scopes_spec.rb +115 -0
- data/spec/models/doorkeeper/access_grant_spec.rb +36 -0
- data/spec/models/doorkeeper/access_token_spec.rb +113 -0
- data/spec/models/doorkeeper/application_spec.rb +129 -0
- data/spec/requests/applications/applications_request_spec.rb +92 -0
- data/spec/requests/applications/authorized_applications_spec.rb +30 -0
- data/spec/requests/endpoints/authorization_spec.rb +63 -0
- data/spec/requests/endpoints/token_spec.rb +29 -0
- data/spec/requests/flows/authorization_code_errors_spec.rb +111 -0
- data/spec/requests/flows/authorization_code_spec.rb +125 -0
- data/spec/requests/flows/client_credentials_spec.rb +56 -0
- data/spec/requests/flows/implicit_grant_errors_spec.rb +31 -0
- data/spec/requests/flows/implicit_grant_spec.rb +19 -0
- data/spec/requests/flows/password_spec.rb +52 -0
- data/spec/requests/flows/refresh_token_spec.rb +66 -0
- data/spec/requests/flows/skip_authorization_spec.rb +40 -0
- data/spec/requests/protected_resources/private_api_spec.rb +43 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/spec_helper_integration.rb +28 -0
- data/spec/support/dependencies/database_cleaner.rb +16 -0
- data/spec/support/dependencies/factory_girl.rb +5 -0
- data/spec/support/helpers/access_token_request_helper.rb +11 -0
- data/spec/support/helpers/authorization_request_helper.rb +32 -0
- data/spec/support/helpers/config_helper.rb +9 -0
- data/spec/support/helpers/model_helper.rb +45 -0
- data/spec/support/helpers/request_spec_helper.rb +72 -0
- data/spec/support/helpers/url_helper.rb +51 -0
- data/spec/support/shared/controllers_shared_context.rb +60 -0
- data/spec/support/shared/models_shared_examples.rb +44 -0
- metadata +163 -57
- data/app/controllers/doorkeeper/application_controller.rbc +0 -675
- data/app/controllers/doorkeeper/applications_controller.rbc +0 -954
- data/app/controllers/doorkeeper/authorizations_controller.rbc +0 -659
- data/app/controllers/doorkeeper/authorized_applications_controller.rbc +0 -393
- data/app/controllers/doorkeeper/tokens_controller.rbc +0 -423
- data/app/helpers/doorkeeper/application_helper.rbc +0 -127
- data/app/models/access_grant.rb +0 -31
- data/app/models/access_grant.rbc +0 -821
- data/app/models/access_token.rb +0 -70
- data/app/models/access_token.rbc +0 -979
- data/app/models/application.rb +0 -45
- data/app/models/application.rbc +0 -753
- data/config/initializers/form_errors.rbc +0 -272
- data/config/routes.rbc +0 -366
- data/lib/doorkeeper/config/scope.rb +0 -11
- data/lib/doorkeeper/config/scopes.rb +0 -61
- data/lib/doorkeeper/config/scopes_builder.rb +0 -18
- data/lib/doorkeeper/config.rbc +0 -1385
- data/lib/doorkeeper/doorkeeper_for.rbc +0 -1029
- data/lib/doorkeeper/engine.rbc +0 -318
- data/lib/doorkeeper/oauth/access_token_request.rbc +0 -2042
- data/lib/doorkeeper/oauth/authorization_request.rbc +0 -2272
- data/lib/doorkeeper/oauth/random_string.rbc +0 -437
- data/lib/doorkeeper/validations.rbc +0 -774
- data/lib/doorkeeper/version.rbc +0 -130
- data/lib/doorkeeper.rbc +0 -407
- data/lib/generators/doorkeeper/install_generator.rbc +0 -453
- 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
|
data/spec/spec_helper.rb
ADDED
|
@@ -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
|