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