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