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,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
module OAuth
|
|
3
5
|
# RFC7662 OAuth 2.0 Token Introspection
|
|
@@ -18,7 +20,17 @@ module Doorkeeper
|
|
|
18
20
|
@error.blank?
|
|
19
21
|
end
|
|
20
22
|
|
|
21
|
-
def
|
|
23
|
+
def error_response
|
|
24
|
+
return if @error.blank?
|
|
25
|
+
|
|
26
|
+
if @error == :invalid_token
|
|
27
|
+
OAuth::InvalidTokenResponse.from_access_token(authorized_token)
|
|
28
|
+
else
|
|
29
|
+
OAuth::ErrorResponse.new(name: @error)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def to_json(*)
|
|
22
34
|
active? ? success_response : failure_response
|
|
23
35
|
end
|
|
24
36
|
|
|
@@ -35,13 +47,29 @@ module Doorkeeper
|
|
|
35
47
|
#
|
|
36
48
|
# @see https://www.oauth.com/oauth2-servers/token-introspection-endpoint/
|
|
37
49
|
#
|
|
50
|
+
# To prevent token scanning attacks, the endpoint MUST also require
|
|
51
|
+
# some form of authorization to access this endpoint, such as client
|
|
52
|
+
# authentication as described in OAuth 2.0 [RFC6749] or a separate
|
|
53
|
+
# OAuth 2.0 access token such as the bearer token described in OAuth
|
|
54
|
+
# 2.0 Bearer Token Usage [RFC6750].
|
|
55
|
+
#
|
|
38
56
|
def authorize!
|
|
39
57
|
# Requested client authorization
|
|
40
58
|
if server.credentials
|
|
41
59
|
@error = :invalid_client unless authorized_client
|
|
42
|
-
|
|
60
|
+
elsif authorized_token
|
|
43
61
|
# Requested bearer token authorization
|
|
44
|
-
|
|
62
|
+
#
|
|
63
|
+
# If the protected resource uses an OAuth 2.0 bearer token to authorize
|
|
64
|
+
# its call to the introspection endpoint and the token used for
|
|
65
|
+
# authorization does not contain sufficient privileges or is otherwise
|
|
66
|
+
# invalid for this request, the authorization server responds with an
|
|
67
|
+
# HTTP 401 code as described in Section 3 of OAuth 2.0 Bearer Token
|
|
68
|
+
# Usage [RFC6750].
|
|
69
|
+
#
|
|
70
|
+
@error = :invalid_token if authorized_token_matches_introspected? || !authorized_token.accessible?
|
|
71
|
+
else
|
|
72
|
+
@error = :invalid_request
|
|
45
73
|
end
|
|
46
74
|
end
|
|
47
75
|
|
|
@@ -58,14 +86,14 @@ module Doorkeeper
|
|
|
58
86
|
|
|
59
87
|
# 2.2. Introspection Response
|
|
60
88
|
def success_response
|
|
61
|
-
|
|
89
|
+
customize_response(
|
|
62
90
|
active: true,
|
|
63
91
|
scope: @token.scopes_string,
|
|
64
92
|
client_id: @token.try(:application).try(:uid),
|
|
65
93
|
token_type: @token.token_type,
|
|
66
94
|
exp: @token.expires_at.to_i,
|
|
67
95
|
iat: @token.created_at.to_i
|
|
68
|
-
|
|
96
|
+
)
|
|
69
97
|
end
|
|
70
98
|
|
|
71
99
|
# If the introspection call is properly authorized but the token is not
|
|
@@ -81,7 +109,7 @@ module Doorkeeper
|
|
|
81
109
|
#
|
|
82
110
|
def failure_response
|
|
83
111
|
{
|
|
84
|
-
active: false
|
|
112
|
+
active: false,
|
|
85
113
|
}
|
|
86
114
|
end
|
|
87
115
|
|
|
@@ -101,6 +129,25 @@ module Doorkeeper
|
|
|
101
129
|
# * The token expired
|
|
102
130
|
# * The token was issued to a different client than is making this request
|
|
103
131
|
#
|
|
132
|
+
# Since resource servers using token introspection rely on the
|
|
133
|
+
# authorization server to determine the state of a token, the
|
|
134
|
+
# authorization server MUST perform all applicable checks against a
|
|
135
|
+
# token's state. For instance, these tests include the following:
|
|
136
|
+
#
|
|
137
|
+
# o If the token can expire, the authorization server MUST determine
|
|
138
|
+
# whether or not the token has expired.
|
|
139
|
+
# o If the token can be issued before it is able to be used, the
|
|
140
|
+
# authorization server MUST determine whether or not a token's valid
|
|
141
|
+
# period has started yet.
|
|
142
|
+
# o If the token can be revoked after it was issued, the authorization
|
|
143
|
+
# server MUST determine whether or not such a revocation has taken
|
|
144
|
+
# place.
|
|
145
|
+
# o If the token has been signed, the authorization server MUST
|
|
146
|
+
# validate the signature.
|
|
147
|
+
# o If the token can be used only at certain resource servers, the
|
|
148
|
+
# authorization server MUST determine whether or not the token can
|
|
149
|
+
# be used at the resource server making the introspection call.
|
|
150
|
+
#
|
|
104
151
|
def active?
|
|
105
152
|
if authorized_client
|
|
106
153
|
valid_token? && authorized_for_client?
|
|
@@ -111,18 +158,39 @@ module Doorkeeper
|
|
|
111
158
|
|
|
112
159
|
# Token can be valid only if it is not expired or revoked.
|
|
113
160
|
def valid_token?
|
|
114
|
-
@token
|
|
161
|
+
@token&.accessible?
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# RFC7662 Section 2.1
|
|
165
|
+
def authorized_token_matches_introspected?
|
|
166
|
+
authorized_token.token == @token&.token
|
|
115
167
|
end
|
|
116
168
|
|
|
117
169
|
# If token doesn't belong to some client, then it is public.
|
|
118
170
|
# Otherwise in it required for token to be connected to the same client.
|
|
119
171
|
def authorized_for_client?
|
|
120
|
-
if @token.application
|
|
172
|
+
if @token.application
|
|
121
173
|
@token.application == authorized_client.application
|
|
122
174
|
else
|
|
123
175
|
true
|
|
124
176
|
end
|
|
125
177
|
end
|
|
178
|
+
|
|
179
|
+
# Allows to customize introspection response.
|
|
180
|
+
# Provides context (controller) and token for generating developer-specific
|
|
181
|
+
# response.
|
|
182
|
+
#
|
|
183
|
+
# @see https://tools.ietf.org/html/rfc7662#section-2.2
|
|
184
|
+
#
|
|
185
|
+
def customize_response(response)
|
|
186
|
+
customized_response = Doorkeeper.configuration.custom_introspection_response.call(
|
|
187
|
+
token,
|
|
188
|
+
server.context
|
|
189
|
+
)
|
|
190
|
+
return response if customized_response.blank?
|
|
191
|
+
|
|
192
|
+
response.merge(customized_response)
|
|
193
|
+
end
|
|
126
194
|
end
|
|
127
195
|
end
|
|
128
196
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
module OAuth
|
|
3
5
|
class TokenResponse
|
|
@@ -9,12 +11,12 @@ module Doorkeeper
|
|
|
9
11
|
|
|
10
12
|
def body
|
|
11
13
|
{
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
"access_token" => token.plaintext_token,
|
|
15
|
+
"token_type" => token.token_type,
|
|
16
|
+
"expires_in" => token.expires_in_seconds,
|
|
17
|
+
"refresh_token" => token.plaintext_refresh_token,
|
|
18
|
+
"scope" => token.scopes_string,
|
|
19
|
+
"created_at" => token.created_at.to_i,
|
|
18
20
|
}.reject { |_, value| value.blank? }
|
|
19
21
|
end
|
|
20
22
|
|
|
@@ -24,9 +26,9 @@ module Doorkeeper
|
|
|
24
26
|
|
|
25
27
|
def headers
|
|
26
28
|
{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
"Cache-Control" => "no-store",
|
|
30
|
+
"Pragma" => "no-cache",
|
|
31
|
+
"Content-Type" => "application/json; charset=utf-8",
|
|
30
32
|
}
|
|
31
33
|
end
|
|
32
34
|
end
|
data/lib/doorkeeper/oauth.rb
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
module Doorkeeper
|
|
4
4
|
module OAuth
|
|
5
5
|
GRANT_TYPES = [
|
|
6
|
-
AUTHORIZATION_CODE =
|
|
7
|
-
IMPLICIT =
|
|
8
|
-
PASSWORD =
|
|
9
|
-
CLIENT_CREDENTIALS =
|
|
10
|
-
REFRESH_TOKEN =
|
|
6
|
+
AUTHORIZATION_CODE = "authorization_code",
|
|
7
|
+
IMPLICIT = "implicit",
|
|
8
|
+
PASSWORD = "password",
|
|
9
|
+
CLIENT_CREDENTIALS = "client_credentials",
|
|
10
|
+
REFRESH_TOKEN = "refresh_token",
|
|
11
11
|
].freeze
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -1,26 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
class AccessGrant < ActiveRecord::Base
|
|
3
|
-
self.table_name = "#{table_name_prefix}oauth_access_grants#{table_name_suffix}"
|
|
5
|
+
self.table_name = "#{table_name_prefix}oauth_access_grants#{table_name_suffix}"
|
|
4
6
|
|
|
5
7
|
include AccessGrantMixin
|
|
6
|
-
include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
|
|
7
|
-
|
|
8
|
-
belongs_to_options = {
|
|
9
|
-
class_name: 'Doorkeeper::Application',
|
|
10
|
-
inverse_of: :access_grants
|
|
11
|
-
}
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
end
|
|
9
|
+
belongs_to :application, class_name: "Doorkeeper::Application",
|
|
10
|
+
optional: true, inverse_of: :access_grants
|
|
16
11
|
|
|
17
|
-
|
|
12
|
+
validates :resource_owner_id,
|
|
13
|
+
:application_id,
|
|
14
|
+
:token,
|
|
15
|
+
:expires_in,
|
|
16
|
+
:redirect_uri,
|
|
17
|
+
presence: true
|
|
18
18
|
|
|
19
|
-
validates :resource_owner_id, :application_id, :token, :expires_in, :redirect_uri, presence: true
|
|
20
19
|
validates :token, uniqueness: true
|
|
21
20
|
|
|
22
21
|
before_validation :generate_token, on: :create
|
|
23
22
|
|
|
23
|
+
# We keep a volatile copy of the raw token for initial communication
|
|
24
|
+
# The stored refresh_token may be mapped and not available in cleartext.
|
|
25
|
+
#
|
|
26
|
+
# Some strategies allow restoring stored secrets (e.g. symmetric encryption)
|
|
27
|
+
# while hashing strategies do not, so you cannot rely on this value
|
|
28
|
+
# returning a present value for persisted tokens.
|
|
29
|
+
def plaintext_token
|
|
30
|
+
if secret_strategy.allows_restoring_secrets?
|
|
31
|
+
secret_strategy.restore_secret(self, :token)
|
|
32
|
+
else
|
|
33
|
+
@raw_token
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
24
37
|
private
|
|
25
38
|
|
|
26
39
|
# Generates token value with UniqueToken class.
|
|
@@ -28,7 +41,8 @@ module Doorkeeper
|
|
|
28
41
|
# @return [String] token value
|
|
29
42
|
#
|
|
30
43
|
def generate_token
|
|
31
|
-
|
|
44
|
+
@raw_token = UniqueToken.generate
|
|
45
|
+
secret_strategy.store_secret(self, :token, @raw_token)
|
|
32
46
|
end
|
|
33
47
|
end
|
|
34
48
|
end
|
|
@@ -1,20 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
class AccessToken < ActiveRecord::Base
|
|
3
|
-
self.table_name = "#{table_name_prefix}oauth_access_tokens#{table_name_suffix}"
|
|
5
|
+
self.table_name = "#{table_name_prefix}oauth_access_tokens#{table_name_suffix}"
|
|
4
6
|
|
|
5
7
|
include AccessTokenMixin
|
|
6
|
-
include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
|
|
7
|
-
|
|
8
|
-
belongs_to_options = {
|
|
9
|
-
class_name: 'Doorkeeper::Application',
|
|
10
|
-
inverse_of: :access_tokens
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
if defined?(ActiveRecord::Base) && ActiveRecord::VERSION::MAJOR >= 5
|
|
14
|
-
belongs_to_options[:optional] = true
|
|
15
|
-
end
|
|
16
8
|
|
|
17
|
-
belongs_to :application,
|
|
9
|
+
belongs_to :application, class_name: "Doorkeeper::Application",
|
|
10
|
+
inverse_of: :access_tokens, optional: true
|
|
18
11
|
|
|
19
12
|
validates :token, presence: true, uniqueness: true
|
|
20
13
|
validates :refresh_token, uniqueness: true, if: :use_refresh_token?
|
|
@@ -41,7 +34,7 @@ module Doorkeeper
|
|
|
41
34
|
end
|
|
42
35
|
|
|
43
36
|
def self.refresh_token_revoked_on_use?
|
|
44
|
-
column_names.include?(
|
|
37
|
+
column_names.include?("previous_refresh_token")
|
|
45
38
|
end
|
|
46
39
|
end
|
|
47
40
|
end
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
class Application < ActiveRecord::Base
|
|
3
|
-
self.table_name = "#{table_name_prefix}oauth_applications#{table_name_suffix}"
|
|
5
|
+
self.table_name = "#{table_name_prefix}oauth_applications#{table_name_suffix}"
|
|
4
6
|
|
|
5
7
|
include ApplicationMixin
|
|
6
|
-
include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
|
|
7
8
|
|
|
8
|
-
has_many :access_grants, dependent: :delete_all, class_name:
|
|
9
|
-
has_many :access_tokens, dependent: :delete_all, class_name:
|
|
9
|
+
has_many :access_grants, dependent: :delete_all, class_name: "Doorkeeper::AccessGrant"
|
|
10
|
+
has_many :access_tokens, dependent: :delete_all, class_name: "Doorkeeper::AccessToken"
|
|
10
11
|
|
|
11
12
|
validates :name, :secret, :uid, presence: true
|
|
12
13
|
validates :uid, uniqueness: true
|
|
@@ -17,7 +18,7 @@ module Doorkeeper
|
|
|
17
18
|
|
|
18
19
|
before_validation :generate_uid, :generate_secret, on: :create
|
|
19
20
|
|
|
20
|
-
has_many :authorized_tokens, -> { where(revoked_at: nil) }, class_name:
|
|
21
|
+
has_many :authorized_tokens, -> { where(revoked_at: nil) }, class_name: "AccessToken"
|
|
21
22
|
has_many :authorized_applications, through: :authorized_tokens, source: :application
|
|
22
23
|
|
|
23
24
|
# Returns Applications associated with active (not revoked) Access Tokens
|
|
@@ -45,6 +46,20 @@ module Doorkeeper
|
|
|
45
46
|
AccessGrant.revoke_all_for(id, resource_owner)
|
|
46
47
|
end
|
|
47
48
|
|
|
49
|
+
# We keep a volatile copy of the raw secret for initial communication
|
|
50
|
+
# The stored refresh_token may be mapped and not available in cleartext.
|
|
51
|
+
#
|
|
52
|
+
# Some strategies allow restoring stored secrets (e.g. symmetric encryption)
|
|
53
|
+
# while hashing strategies do not, so you cannot rely on this value
|
|
54
|
+
# returning a present value for persisted tokens.
|
|
55
|
+
def plaintext_secret
|
|
56
|
+
if secret_strategy.allows_restoring_secrets?
|
|
57
|
+
secret_strategy.restore_secret(self, :secret)
|
|
58
|
+
else
|
|
59
|
+
@raw_secret
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
48
63
|
private
|
|
49
64
|
|
|
50
65
|
def generate_uid
|
|
@@ -52,12 +67,16 @@ module Doorkeeper
|
|
|
52
67
|
end
|
|
53
68
|
|
|
54
69
|
def generate_secret
|
|
55
|
-
|
|
70
|
+
return unless secret.blank?
|
|
71
|
+
|
|
72
|
+
@raw_secret = UniqueToken.generate
|
|
73
|
+
secret_strategy.store_secret(self, :secret, @raw_secret)
|
|
56
74
|
end
|
|
57
75
|
|
|
58
76
|
def scopes_match_configured
|
|
59
77
|
if scopes.present? &&
|
|
60
|
-
!ScopeChecker.valid?(scopes.to_s,
|
|
78
|
+
!ScopeChecker.valid?(scope_str: scopes.to_s,
|
|
79
|
+
server_scopes: Doorkeeper.configuration.scopes)
|
|
61
80
|
errors.add(:scopes, :not_match_configured)
|
|
62
81
|
end
|
|
63
82
|
end
|
|
@@ -3,22 +3,29 @@
|
|
|
3
3
|
module Doorkeeper
|
|
4
4
|
module Orm
|
|
5
5
|
module ActiveRecord
|
|
6
|
+
# Helper class to clear stale and non-active tokens and grants.
|
|
7
|
+
# Used by Doorkeeper Rake tasks.
|
|
8
|
+
#
|
|
6
9
|
class StaleRecordsCleaner
|
|
7
10
|
def initialize(base_scope)
|
|
8
11
|
@base_scope = base_scope
|
|
9
12
|
end
|
|
10
13
|
|
|
14
|
+
# Clears revoked records
|
|
11
15
|
def clean_revoked
|
|
12
16
|
table = @base_scope.arel_table
|
|
17
|
+
|
|
13
18
|
@base_scope.where.not(revoked_at: nil)
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
.where(table[:revoked_at].lt(Time.current))
|
|
20
|
+
.in_batches(&:delete_all)
|
|
16
21
|
end
|
|
17
22
|
|
|
23
|
+
# Clears expired records
|
|
18
24
|
def clean_expired(ttl)
|
|
19
25
|
table = @base_scope.arel_table
|
|
26
|
+
|
|
20
27
|
@base_scope.where(table[:created_at].lt(Time.current - ttl))
|
|
21
|
-
|
|
28
|
+
.in_batches(&:delete_all)
|
|
22
29
|
end
|
|
23
30
|
end
|
|
24
31
|
end
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "active_support/lazy_load_hooks"
|
|
4
|
+
|
|
5
|
+
require "doorkeeper/orm/active_record/stale_records_cleaner"
|
|
4
6
|
|
|
5
7
|
module Doorkeeper
|
|
6
8
|
module Orm
|
|
7
9
|
module ActiveRecord
|
|
8
10
|
def self.initialize_models!
|
|
9
11
|
lazy_load do
|
|
10
|
-
require
|
|
11
|
-
require
|
|
12
|
-
require
|
|
12
|
+
require "doorkeeper/orm/active_record/access_grant"
|
|
13
|
+
require "doorkeeper/orm/active_record/access_token"
|
|
14
|
+
require "doorkeeper/orm/active_record/application"
|
|
13
15
|
|
|
14
16
|
if Doorkeeper.configuration.active_record_options[:establish_connection]
|
|
15
17
|
[Doorkeeper::AccessGrant, Doorkeeper::AccessToken, Doorkeeper::Application].each do |model|
|
|
@@ -22,7 +24,7 @@ module Doorkeeper
|
|
|
22
24
|
|
|
23
25
|
def self.initialize_application_owner!
|
|
24
26
|
lazy_load do
|
|
25
|
-
require
|
|
27
|
+
require "doorkeeper/models/concerns/ownership"
|
|
26
28
|
|
|
27
29
|
Doorkeeper::Application.send :include, Doorkeeper::Models::Ownership
|
|
28
30
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
module Rails
|
|
3
5
|
module Helpers
|
|
@@ -12,13 +14,15 @@ module Doorkeeper
|
|
|
12
14
|
def doorkeeper_forbidden_render_options(**); end
|
|
13
15
|
|
|
14
16
|
def valid_doorkeeper_token?
|
|
15
|
-
doorkeeper_token
|
|
17
|
+
doorkeeper_token&.acceptable?(@_doorkeeper_scopes)
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
private
|
|
19
21
|
|
|
20
22
|
def doorkeeper_render_error
|
|
21
23
|
error = doorkeeper_error
|
|
24
|
+
error.raise_exception! if Doorkeeper.configuration.raise_on_errors?
|
|
25
|
+
|
|
22
26
|
headers.merge!(error.headers.reject { |k| k == "Content-Type" })
|
|
23
27
|
doorkeeper_render_error_with(error)
|
|
24
28
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Doorkeeper
|
|
2
4
|
module Rails
|
|
3
5
|
class Routes # :nodoc:
|
|
@@ -6,17 +8,17 @@ module Doorkeeper
|
|
|
6
8
|
|
|
7
9
|
def initialize
|
|
8
10
|
@controllers = {
|
|
9
|
-
authorizations:
|
|
10
|
-
applications:
|
|
11
|
-
authorized_applications:
|
|
12
|
-
tokens:
|
|
13
|
-
token_info:
|
|
11
|
+
authorizations: "doorkeeper/authorizations",
|
|
12
|
+
applications: "doorkeeper/applications",
|
|
13
|
+
authorized_applications: "doorkeeper/authorized_applications",
|
|
14
|
+
tokens: "doorkeeper/tokens",
|
|
15
|
+
token_info: "doorkeeper/token_info",
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
@as = {
|
|
17
19
|
authorizations: :authorization,
|
|
18
20
|
tokens: :token,
|
|
19
|
-
token_info: :token_info
|
|
21
|
+
token_info: :token_info,
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
@skips = []
|
|
@@ -25,7 +27,7 @@ module Doorkeeper
|
|
|
25
27
|
def [](routes)
|
|
26
28
|
{
|
|
27
29
|
controllers: @controllers[routes],
|
|
28
|
-
as: @as[routes]
|
|
30
|
+
as: @as[routes],
|
|
29
31
|
}
|
|
30
32
|
end
|
|
31
33
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "doorkeeper/rails/routes/mapping"
|
|
4
|
+
require "doorkeeper/rails/routes/mapper"
|
|
3
5
|
|
|
4
6
|
module Doorkeeper
|
|
5
7
|
module Rails
|
|
@@ -30,7 +32,7 @@ module Doorkeeper
|
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
def generate_routes!(options)
|
|
33
|
-
routes.scope options[:scope] ||
|
|
35
|
+
routes.scope options[:scope] || "oauth", as: "oauth" do
|
|
34
36
|
map_route(:authorizations, :authorization_routes)
|
|
35
37
|
map_route(:tokens, :token_routes)
|
|
36
38
|
map_route(:tokens, :revoke_routes)
|
|
@@ -54,48 +56,53 @@ module Doorkeeper
|
|
|
54
56
|
def authorization_routes(mapping)
|
|
55
57
|
routes.resource(
|
|
56
58
|
:authorization,
|
|
57
|
-
path:
|
|
59
|
+
path: "authorize",
|
|
58
60
|
only: %i[create destroy],
|
|
59
61
|
as: mapping[:as],
|
|
60
62
|
controller: mapping[:controllers]
|
|
61
63
|
) do
|
|
62
|
-
routes.get
|
|
63
|
-
routes.get
|
|
64
|
+
routes.get "/native", action: :show, on: :member
|
|
65
|
+
routes.get "/", action: :new, on: :member
|
|
64
66
|
end
|
|
65
67
|
end
|
|
66
68
|
|
|
67
69
|
def token_routes(mapping)
|
|
68
70
|
routes.resource(
|
|
69
71
|
:token,
|
|
70
|
-
path:
|
|
72
|
+
path: "token",
|
|
71
73
|
only: [:create], as: mapping[:as],
|
|
72
74
|
controller: mapping[:controllers]
|
|
73
75
|
)
|
|
74
76
|
end
|
|
75
77
|
|
|
76
78
|
def revoke_routes(mapping)
|
|
77
|
-
routes.post
|
|
79
|
+
routes.post "revoke", controller: mapping[:controllers], action: :revoke
|
|
78
80
|
end
|
|
79
81
|
|
|
80
82
|
def introspect_routes(mapping)
|
|
81
|
-
routes.post
|
|
83
|
+
routes.post "introspect", controller: mapping[:controllers], action: :introspect
|
|
82
84
|
end
|
|
83
85
|
|
|
84
86
|
def token_info_routes(mapping)
|
|
85
87
|
routes.resource(
|
|
86
88
|
:token_info,
|
|
87
|
-
path:
|
|
89
|
+
path: "token/info",
|
|
88
90
|
only: [:show], as: mapping[:as],
|
|
89
91
|
controller: mapping[:controllers]
|
|
90
92
|
)
|
|
91
93
|
end
|
|
92
94
|
|
|
93
95
|
def application_routes(mapping)
|
|
94
|
-
routes.resources :doorkeeper_applications,
|
|
96
|
+
routes.resources :doorkeeper_applications,
|
|
97
|
+
controller: mapping[:controllers],
|
|
98
|
+
as: :applications,
|
|
99
|
+
path: "applications"
|
|
95
100
|
end
|
|
96
101
|
|
|
97
102
|
def authorized_applications_routes(mapping)
|
|
98
|
-
routes.resources :authorized_applications,
|
|
103
|
+
routes.resources :authorized_applications,
|
|
104
|
+
only: %i[index destroy],
|
|
105
|
+
controller: mapping[:controllers]
|
|
99
106
|
end
|
|
100
107
|
end
|
|
101
108
|
end
|
data/lib/doorkeeper/rake/db.rake
CHANGED
|
@@ -2,37 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
namespace :doorkeeper do
|
|
4
4
|
namespace :db do
|
|
5
|
-
desc
|
|
5
|
+
desc "Removes stale data from doorkeeper related database tables"
|
|
6
6
|
task cleanup: [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
"doorkeeper:db:cleanup:revoked_tokens",
|
|
8
|
+
"doorkeeper:db:cleanup:expired_tokens",
|
|
9
|
+
"doorkeeper:db:cleanup:revoked_grants",
|
|
10
|
+
"doorkeeper:db:cleanup:expired_grants",
|
|
11
11
|
]
|
|
12
12
|
|
|
13
13
|
namespace :cleanup do
|
|
14
|
-
desc
|
|
15
|
-
task revoked_tokens:
|
|
16
|
-
cleaner = Doorkeeper::
|
|
14
|
+
desc "Removes stale access tokens"
|
|
15
|
+
task revoked_tokens: "doorkeeper:setup" do
|
|
16
|
+
cleaner = Doorkeeper::StaleRecordsCleaner.new(Doorkeeper::AccessToken)
|
|
17
17
|
cleaner.clean_revoked
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
desc
|
|
21
|
-
task expired_tokens:
|
|
20
|
+
desc "Removes expired (TTL passed) access tokens"
|
|
21
|
+
task expired_tokens: "doorkeeper:setup" do
|
|
22
22
|
expirable_tokens = Doorkeeper::AccessToken.where(refresh_token: nil)
|
|
23
|
-
cleaner = Doorkeeper::
|
|
23
|
+
cleaner = Doorkeeper::StaleRecordsCleaner.new(expirable_tokens)
|
|
24
24
|
cleaner.clean_expired(Doorkeeper.configuration.access_token_expires_in)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
desc
|
|
28
|
-
task revoked_grants:
|
|
29
|
-
cleaner = Doorkeeper::
|
|
27
|
+
desc "Removes stale access grants"
|
|
28
|
+
task revoked_grants: "doorkeeper:setup" do
|
|
29
|
+
cleaner = Doorkeeper::StaleRecordsCleaner.new(Doorkeeper::AccessGrant)
|
|
30
30
|
cleaner.clean_revoked
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
desc
|
|
34
|
-
task expired_grants:
|
|
35
|
-
cleaner = Doorkeeper::
|
|
33
|
+
desc "Removes expired (TTL passed) access grants"
|
|
34
|
+
task expired_grants: "doorkeeper:setup" do
|
|
35
|
+
cleaner = Doorkeeper::StaleRecordsCleaner.new(Doorkeeper::AccessGrant)
|
|
36
36
|
cleaner.clean_expired(Doorkeeper.configuration.authorization_code_expires_in)
|
|
37
37
|
end
|
|
38
38
|
end
|
data/lib/doorkeeper/rake.rb
CHANGED