super-smart-kit 0.0.1
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 +7 -0
- data/doorkeeper-5.9.3/CHANGELOG.md +1208 -0
- data/doorkeeper-5.9.3/MIT-LICENSE +20 -0
- data/doorkeeper-5.9.3/README.md +186 -0
- data/doorkeeper-5.9.3/app/assets/stylesheets/doorkeeper/admin/application.css +10 -0
- data/doorkeeper-5.9.3/app/assets/stylesheets/doorkeeper/application.css +64 -0
- data/doorkeeper-5.9.3/app/controllers/doorkeeper/application_controller.rb +14 -0
- data/doorkeeper-5.9.3/app/controllers/doorkeeper/application_metal_controller.rb +13 -0
- data/doorkeeper-5.9.3/app/controllers/doorkeeper/applications_controller.rb +99 -0
- data/doorkeeper-5.9.3/app/controllers/doorkeeper/authorizations_controller.rb +164 -0
- data/doorkeeper-5.9.3/app/controllers/doorkeeper/authorized_applications_controller.rb +33 -0
- data/doorkeeper-5.9.3/app/controllers/doorkeeper/token_info_controller.rb +25 -0
- data/doorkeeper-5.9.3/app/controllers/doorkeeper/tokens_controller.rb +180 -0
- data/doorkeeper-5.9.3/app/helpers/doorkeeper/dashboard_helper.rb +21 -0
- data/doorkeeper-5.9.3/app/views/doorkeeper/applications/_delete_form.html.erb +6 -0
- data/doorkeeper-5.9.3/app/views/doorkeeper/applications/_form.html.erb +59 -0
- data/doorkeeper-5.9.3/app/views/doorkeeper/applications/edit.html.erb +5 -0
- data/doorkeeper-5.9.3/app/views/doorkeeper/applications/index.html.erb +38 -0
- data/doorkeeper-5.9.3/app/views/doorkeeper/applications/new.html.erb +5 -0
- data/doorkeeper-5.9.3/app/views/doorkeeper/applications/show.html.erb +63 -0
- data/doorkeeper-5.9.3/app/views/doorkeeper/authorizations/error.html.erb +9 -0
- data/doorkeeper-5.9.3/app/views/doorkeeper/authorizations/form_post.html.erb +15 -0
- data/doorkeeper-5.9.3/app/views/doorkeeper/authorizations/new.html.erb +46 -0
- data/doorkeeper-5.9.3/app/views/doorkeeper/authorizations/show.html.erb +7 -0
- data/doorkeeper-5.9.3/app/views/doorkeeper/authorized_applications/_delete_form.html.erb +4 -0
- data/doorkeeper-5.9.3/app/views/doorkeeper/authorized_applications/index.html.erb +24 -0
- data/doorkeeper-5.9.3/app/views/layouts/doorkeeper/admin.html.erb +39 -0
- data/doorkeeper-5.9.3/app/views/layouts/doorkeeper/application.html.erb +23 -0
- data/doorkeeper-5.9.3/config/locales/en.yml +155 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/config/abstract_builder.rb +28 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/config/option.rb +82 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/config/validations.rb +65 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/config.rb +721 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/engine.rb +38 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/errors.rb +85 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/grant_flow/fallback_flow.rb +15 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/grant_flow/flow.rb +44 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/grant_flow/registry.rb +50 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/grant_flow.rb +45 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/grape/authorization_decorator.rb +19 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/grape/helpers.rb +58 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/helpers/controller.rb +91 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/models/access_grant_mixin.rb +124 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/models/access_token_mixin.rb +526 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/models/application_mixin.rb +96 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/accessible.rb +15 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/expirable.rb +36 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/expiration_time_sql_math.rb +96 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/orderable.rb +15 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/ownership.rb +18 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/polymorphic_resource_owner.rb +29 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/resource_ownerable.rb +47 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/reusable.rb +19 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/revocable.rb +31 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/scopes.rb +27 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/secret_storable.rb +106 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/write_to_primary.rb +57 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/authorization/code.rb +76 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/authorization/context.rb +17 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/authorization/token.rb +100 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/authorization/uri_builder.rb +33 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/authorization_code_request.rb +125 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/base_request.rb +68 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/base_response.rb +31 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/client/credentials.rb +34 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/client.rb +28 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/client_credentials/creator.rb +57 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/client_credentials/issuer.rb +48 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/client_credentials/validator.rb +55 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/client_credentials_request.rb +46 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/code_request.rb +25 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/code_response.rb +51 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/error.rb +16 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/error_response.rb +121 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/forbidden_token_response.rb +31 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/helpers/scope_checker.rb +50 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/helpers/unique_token.rb +30 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/helpers/uri_checker.rb +83 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/hooks/context.rb +21 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/invalid_request_response.rb +47 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/invalid_token_response.rb +54 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/nonstandard.rb +39 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/password_access_token_request.rb +75 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/pre_authorization.rb +187 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/refresh_token_request.rb +144 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/scopes.rb +134 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/token.rb +66 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/token_introspection.rb +220 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/token_request.rb +25 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth/token_response.rb +38 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/oauth.rb +13 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/access_grant.rb +9 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/access_token.rb +9 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/application.rb +10 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +64 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/mixins/access_token.rb +95 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/mixins/application.rb +223 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +66 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +44 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record.rb +41 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/rails/helpers.rb +82 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/rails/routes/abstract_router.rb +35 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/rails/routes/mapper.rb +30 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/rails/routes/mapping.rb +40 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/rails/routes/registry.rb +45 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/rails/routes.rb +110 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/rake/db.rake +40 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/rake/setup.rake +6 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/rake.rb +14 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/request/authorization_code.rb +26 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/request/client_credentials.rb +17 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/request/code.rb +17 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/request/password.rb +19 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/request/refresh_token.rb +22 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/request/strategy.rb +19 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/request/token.rb +17 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/request.rb +73 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/revocable_tokens/revocable_access_token.rb +21 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/revocable_tokens/revocable_refresh_token.rb +21 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/secret_storing/base.rb +64 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/secret_storing/plain.rb +33 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/server.rb +44 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/stale_records_cleaner.rb +24 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/validations.rb +33 -0
- data/doorkeeper-5.9.3/lib/doorkeeper/version.rb +14 -0
- data/doorkeeper-5.9.3/lib/doorkeeper.rb +209 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/application_owner_generator.rb +33 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/confidential_applications_generator.rb +33 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb +39 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/install_generator.rb +22 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/migration_generator.rb +32 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/pkce_generator.rb +33 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/previous_refresh_token_generator.rb +41 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/remove_applications_secret_not_null_constraint_generator.rb +33 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/README +24 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb +9 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb +13 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +8 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb +17 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/initializer.rb +544 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/migration.rb.erb +99 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/remove_applications_secret_not_null_constraint.rb.erb +7 -0
- data/doorkeeper-5.9.3/lib/generators/doorkeeper/views_generator.rb +18 -0
- data/doorkeeper-5.9.3/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +6 -0
- data/super-smart-kit.gemspec +12 -0
- metadata +188 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doorkeeper
|
|
4
|
+
module OAuth
|
|
5
|
+
# RFC7662 OAuth 2.0 Token Introspection
|
|
6
|
+
#
|
|
7
|
+
# @see https://datatracker.ietf.org/doc/html/rfc7662
|
|
8
|
+
class TokenIntrospection
|
|
9
|
+
attr_reader :token, :error, :invalid_request_reason
|
|
10
|
+
|
|
11
|
+
def initialize(server, token)
|
|
12
|
+
@server = server
|
|
13
|
+
@token = token
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def authorized?
|
|
17
|
+
authorize!
|
|
18
|
+
@error.blank?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def error_response
|
|
22
|
+
return if @error.blank?
|
|
23
|
+
|
|
24
|
+
if @error == Errors::InvalidToken
|
|
25
|
+
OAuth::InvalidTokenResponse.from_access_token(authorized_token)
|
|
26
|
+
elsif @error == Errors::InvalidRequest
|
|
27
|
+
OAuth::InvalidRequestResponse.from_request(self)
|
|
28
|
+
else
|
|
29
|
+
OAuth::ErrorResponse.from_request(self)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def to_json(*)
|
|
34
|
+
active? ? success_response : failure_response
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
attr_reader :server
|
|
40
|
+
|
|
41
|
+
# If the protected resource uses OAuth 2.0 client credentials to
|
|
42
|
+
# authenticate to the introspection endpoint and its credentials are
|
|
43
|
+
# invalid, the authorization server responds with an HTTP 401
|
|
44
|
+
# (Unauthorized) as described in Section 5.2 of OAuth 2.0 [RFC6749].
|
|
45
|
+
#
|
|
46
|
+
# Endpoint must first validate the authentication.
|
|
47
|
+
# If the authentication is invalid, the endpoint should respond with
|
|
48
|
+
# an HTTP 401 status code and an invalid_client response.
|
|
49
|
+
#
|
|
50
|
+
# @see https://www.oauth.com/oauth2-servers/token-introspection-endpoint/
|
|
51
|
+
#
|
|
52
|
+
# To prevent token scanning attacks, the endpoint MUST also require
|
|
53
|
+
# some form of authorization to access this endpoint, such as client
|
|
54
|
+
# authentication as described in OAuth 2.0 [RFC6749] or a separate
|
|
55
|
+
# OAuth 2.0 access token such as the bearer token described in OAuth
|
|
56
|
+
# 2.0 Bearer Token Usage [RFC6750].
|
|
57
|
+
#
|
|
58
|
+
def authorize!
|
|
59
|
+
# Requested client authorization
|
|
60
|
+
if server.credentials
|
|
61
|
+
authorize_using_basic_auth!
|
|
62
|
+
elsif authorized_token
|
|
63
|
+
authorize_using_bearer_token!
|
|
64
|
+
else
|
|
65
|
+
@error = Errors::InvalidRequest
|
|
66
|
+
@invalid_request_reason = :request_not_authorized
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def authorize_using_basic_auth!
|
|
71
|
+
# Note that a properly formed and authorized query for an inactive or
|
|
72
|
+
# otherwise invalid token (or a token the protected resource is not
|
|
73
|
+
# allowed to know about) is not considered an error response by this
|
|
74
|
+
# specification. In these cases, the authorization server MUST instead
|
|
75
|
+
# respond with an introspection response with the "active" field set to
|
|
76
|
+
# "false" as described in Section 2.2.
|
|
77
|
+
@error = Errors::InvalidClient unless authorized_client
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def authorize_using_bearer_token!
|
|
81
|
+
# Requested bearer token authorization
|
|
82
|
+
#
|
|
83
|
+
# If the protected resource uses an OAuth 2.0 bearer token to authorize
|
|
84
|
+
# its call to the introspection endpoint and the token used for
|
|
85
|
+
# authorization does not contain sufficient privileges or is otherwise
|
|
86
|
+
# invalid for this request, the authorization server responds with an
|
|
87
|
+
# HTTP 401 code as described in Section 3 of OAuth 2.0 Bearer Token
|
|
88
|
+
# Usage [RFC6750].
|
|
89
|
+
#
|
|
90
|
+
@error = Errors::InvalidToken unless valid_authorized_token?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Client Authentication
|
|
94
|
+
def authorized_client
|
|
95
|
+
@authorized_client ||= server.credentials && server.client
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Bearer Token Authentication
|
|
99
|
+
def authorized_token
|
|
100
|
+
@authorized_token ||= Doorkeeper.authenticate(server.context.request)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# 2.2. Introspection Response
|
|
104
|
+
def success_response
|
|
105
|
+
response = {
|
|
106
|
+
active: true,
|
|
107
|
+
scope: @token.scopes_string,
|
|
108
|
+
client_id: @token.try(:application).try(:uid),
|
|
109
|
+
token_type: @token.token_type,
|
|
110
|
+
iat: @token.created_at.to_i,
|
|
111
|
+
}
|
|
112
|
+
# `exp` is OPTIONAL per RFC 7662 §2.2; omit it for non-expiring tokens
|
|
113
|
+
# so clients don't interpret `0` as "expired at 1970-01-01".
|
|
114
|
+
response[:exp] = @token.expires_at.to_i if @token.expires_at
|
|
115
|
+
|
|
116
|
+
customize_response(response)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# If the introspection call is properly authorized but the token is not
|
|
120
|
+
# active, does not exist on this server, or the protected resource is
|
|
121
|
+
# not allowed to introspect this particular token, then the
|
|
122
|
+
# authorization server MUST return an introspection response with the
|
|
123
|
+
# "active" field set to "false". Note that to avoid disclosing too
|
|
124
|
+
# much of the authorization server's state to a third party, the
|
|
125
|
+
# authorization server SHOULD NOT include any additional information
|
|
126
|
+
# about an inactive token, including why the token is inactive.
|
|
127
|
+
#
|
|
128
|
+
# @see https://datatracker.ietf.org/doc/html/rfc7662 2.2. Introspection Response
|
|
129
|
+
#
|
|
130
|
+
def failure_response
|
|
131
|
+
{
|
|
132
|
+
active: false,
|
|
133
|
+
}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Boolean indicator of whether or not the presented token
|
|
137
|
+
# is currently active. The specifics of a token's "active" state
|
|
138
|
+
# will vary depending on the implementation of the authorization
|
|
139
|
+
# server and the information it keeps about its tokens, but a "true"
|
|
140
|
+
# value return for the "active" property will generally indicate
|
|
141
|
+
# that a given token has been issued by this authorization server,
|
|
142
|
+
# has not been revoked by the resource owner, and is within its
|
|
143
|
+
# given time window of validity (e.g., after its issuance time and
|
|
144
|
+
# before its expiration time).
|
|
145
|
+
#
|
|
146
|
+
# Any other error is considered an "inactive" token.
|
|
147
|
+
#
|
|
148
|
+
# * The token requested does not exist or is invalid
|
|
149
|
+
# * The token expired
|
|
150
|
+
# * The token was issued to a different client than is making this request
|
|
151
|
+
#
|
|
152
|
+
# Since resource servers using token introspection rely on the
|
|
153
|
+
# authorization server to determine the state of a token, the
|
|
154
|
+
# authorization server MUST perform all applicable checks against a
|
|
155
|
+
# token's state. For instance, these tests include the following:
|
|
156
|
+
#
|
|
157
|
+
# o If the token can expire, the authorization server MUST determine
|
|
158
|
+
# whether or not the token has expired.
|
|
159
|
+
# o If the token can be issued before it is able to be used, the
|
|
160
|
+
# authorization server MUST determine whether or not a token's valid
|
|
161
|
+
# period has started yet.
|
|
162
|
+
# o If the token can be revoked after it was issued, the authorization
|
|
163
|
+
# server MUST determine whether or not such a revocation has taken
|
|
164
|
+
# place.
|
|
165
|
+
# o If the token has been signed, the authorization server MUST
|
|
166
|
+
# validate the signature.
|
|
167
|
+
# o If the token can be used only at certain resource servers, the
|
|
168
|
+
# authorization server MUST determine whether or not the token can
|
|
169
|
+
# be used at the resource server making the introspection call.
|
|
170
|
+
#
|
|
171
|
+
def active?
|
|
172
|
+
if authorized_client
|
|
173
|
+
valid_token? && token_introspection_allowed?(auth_client: authorized_client.application)
|
|
174
|
+
else
|
|
175
|
+
valid_token?
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Token can be valid only if it is not expired or revoked.
|
|
180
|
+
def valid_token?
|
|
181
|
+
@token&.accessible?
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def valid_authorized_token?
|
|
185
|
+
!authorized_token_matches_introspected? &&
|
|
186
|
+
authorized_token.accessible? &&
|
|
187
|
+
token_introspection_allowed?(auth_token: authorized_token)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# RFC7662 Section 2.1
|
|
191
|
+
def authorized_token_matches_introspected?
|
|
192
|
+
authorized_token.token == @token&.token
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Config constraints for introspection in Doorkeeper.config.allow_token_introspection
|
|
196
|
+
def token_introspection_allowed?(auth_client: nil, auth_token: nil)
|
|
197
|
+
allow_introspection = Doorkeeper.config.allow_token_introspection
|
|
198
|
+
return allow_introspection unless allow_introspection.respond_to?(:call)
|
|
199
|
+
|
|
200
|
+
allow_introspection.call(@token, auth_client, auth_token)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Allows to customize introspection response.
|
|
204
|
+
# Provides context (controller) and token for generating developer-specific
|
|
205
|
+
# response.
|
|
206
|
+
#
|
|
207
|
+
# @see https://datatracker.ietf.org/doc/html/rfc7662#section-2.2
|
|
208
|
+
#
|
|
209
|
+
def customize_response(response)
|
|
210
|
+
customized_response = Doorkeeper.config.custom_introspection_response.call(
|
|
211
|
+
token,
|
|
212
|
+
server.context,
|
|
213
|
+
)
|
|
214
|
+
return response if customized_response.blank?
|
|
215
|
+
|
|
216
|
+
response.merge(customized_response)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doorkeeper
|
|
4
|
+
module OAuth
|
|
5
|
+
class TokenRequest
|
|
6
|
+
attr_reader :pre_auth, :resource_owner
|
|
7
|
+
|
|
8
|
+
def initialize(pre_auth, resource_owner)
|
|
9
|
+
@pre_auth = pre_auth
|
|
10
|
+
@resource_owner = resource_owner
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def authorize
|
|
14
|
+
auth = Authorization::Token.new(pre_auth, resource_owner)
|
|
15
|
+
auth.issue_token!
|
|
16
|
+
CodeResponse.new(pre_auth, auth, response_on_fragment: true)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def deny
|
|
20
|
+
pre_auth.error = Errors::AccessDenied
|
|
21
|
+
pre_auth.error_response
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doorkeeper
|
|
4
|
+
module OAuth
|
|
5
|
+
class TokenResponse
|
|
6
|
+
attr_reader :token
|
|
7
|
+
|
|
8
|
+
alias issued_token token
|
|
9
|
+
|
|
10
|
+
def initialize(token)
|
|
11
|
+
@token = token
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def body
|
|
15
|
+
@body ||= {
|
|
16
|
+
"access_token" => token.plaintext_token,
|
|
17
|
+
"token_type" => token.token_type,
|
|
18
|
+
"expires_in" => token.expires_in_seconds,
|
|
19
|
+
"refresh_token" => token.plaintext_refresh_token,
|
|
20
|
+
"scope" => token.scopes_string,
|
|
21
|
+
"created_at" => token.created_at.to_i,
|
|
22
|
+
}.reject { |_, value| value.blank? }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def status
|
|
26
|
+
:ok
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def headers
|
|
30
|
+
{
|
|
31
|
+
"Cache-Control" => "no-store, no-cache",
|
|
32
|
+
"Content-Type" => "application/json; charset=utf-8",
|
|
33
|
+
"Pragma" => "no-cache",
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doorkeeper
|
|
4
|
+
module OAuth
|
|
5
|
+
GRANT_TYPES = [
|
|
6
|
+
AUTHORIZATION_CODE = "authorization_code",
|
|
7
|
+
IMPLICIT = "implicit",
|
|
8
|
+
PASSWORD = "password",
|
|
9
|
+
CLIENT_CREDENTIALS = "client_credentials",
|
|
10
|
+
REFRESH_TOKEN = "refresh_token",
|
|
11
|
+
].freeze
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "doorkeeper/orm/active_record/redirect_uri_validator"
|
|
4
|
+
require "doorkeeper/orm/active_record/mixins/application"
|
|
5
|
+
|
|
6
|
+
module Doorkeeper
|
|
7
|
+
class Application < ::ActiveRecord::Base
|
|
8
|
+
include ::Doorkeeper::Orm::ActiveRecord::Mixins::Application
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doorkeeper::Orm::ActiveRecord::Mixins
|
|
4
|
+
module AccessGrant
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
self.table_name = compute_doorkeeper_table_name
|
|
9
|
+
self.strict_loading_by_default = false if respond_to?(:strict_loading_by_default)
|
|
10
|
+
|
|
11
|
+
include ::Doorkeeper::AccessGrantMixin
|
|
12
|
+
include ::Doorkeeper::Models::PolymorphicResourceOwner::ForAccessGrant
|
|
13
|
+
|
|
14
|
+
belongs_to :application, class_name: Doorkeeper.config.application_class.to_s,
|
|
15
|
+
optional: true,
|
|
16
|
+
inverse_of: :access_grants
|
|
17
|
+
|
|
18
|
+
validates :application_id,
|
|
19
|
+
:token,
|
|
20
|
+
:expires_in,
|
|
21
|
+
:redirect_uri,
|
|
22
|
+
presence: true
|
|
23
|
+
|
|
24
|
+
validates :token, uniqueness: { case_sensitive: true }
|
|
25
|
+
|
|
26
|
+
before_validation :generate_token, on: :create
|
|
27
|
+
|
|
28
|
+
# We keep a volatile copy of the raw token for initial communication
|
|
29
|
+
# The stored refresh_token may be mapped and not available in cleartext.
|
|
30
|
+
#
|
|
31
|
+
# Some strategies allow restoring stored secrets (e.g. symmetric encryption)
|
|
32
|
+
# while hashing strategies do not, so you cannot rely on this value
|
|
33
|
+
# returning a present value for persisted tokens.
|
|
34
|
+
def plaintext_token
|
|
35
|
+
if secret_strategy.allows_restoring_secrets?
|
|
36
|
+
secret_strategy.restore_secret(self, :token)
|
|
37
|
+
else
|
|
38
|
+
@raw_token
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
# Generates token value with UniqueToken class.
|
|
45
|
+
#
|
|
46
|
+
# @return [String] token value
|
|
47
|
+
#
|
|
48
|
+
def generate_token
|
|
49
|
+
@raw_token = Doorkeeper::OAuth::Helpers::UniqueToken.generate
|
|
50
|
+
secret_strategy.store_secret(self, :token, @raw_token)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
module ClassMethods
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def compute_doorkeeper_table_name
|
|
58
|
+
table_name = "oauth_access_grant"
|
|
59
|
+
table_name = table_name.pluralize if pluralize_table_names
|
|
60
|
+
"#{table_name_prefix}#{table_name}#{table_name_suffix}"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doorkeeper::Orm::ActiveRecord::Mixins
|
|
4
|
+
module AccessToken
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
self.table_name = compute_doorkeeper_table_name
|
|
9
|
+
self.strict_loading_by_default = false if respond_to?(:strict_loading_by_default)
|
|
10
|
+
|
|
11
|
+
include ::Doorkeeper::AccessTokenMixin
|
|
12
|
+
include ::Doorkeeper::Models::PolymorphicResourceOwner::ForAccessToken
|
|
13
|
+
|
|
14
|
+
belongs_to :application, class_name: Doorkeeper.config.application_class.to_s,
|
|
15
|
+
inverse_of: :access_tokens,
|
|
16
|
+
optional: true
|
|
17
|
+
|
|
18
|
+
validates :token, presence: true, uniqueness: { case_sensitive: true }
|
|
19
|
+
validates :refresh_token, uniqueness: { case_sensitive: true }, if: :use_refresh_token?
|
|
20
|
+
|
|
21
|
+
# @attr_writer [Boolean, nil] use_refresh_token
|
|
22
|
+
# indicates the possibility of using refresh token
|
|
23
|
+
attr_writer :use_refresh_token
|
|
24
|
+
|
|
25
|
+
before_validation :generate_token, on: :create
|
|
26
|
+
before_validation :generate_refresh_token,
|
|
27
|
+
on: :create, if: :use_refresh_token?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
module ClassMethods
|
|
31
|
+
# Searches for not revoked Access Tokens associated with the
|
|
32
|
+
# specific Resource Owner.
|
|
33
|
+
#
|
|
34
|
+
# @param resource_owner [ActiveRecord::Base]
|
|
35
|
+
# Resource Owner model instance
|
|
36
|
+
#
|
|
37
|
+
# @return [ActiveRecord::Relation]
|
|
38
|
+
# active Access Tokens for Resource Owner
|
|
39
|
+
#
|
|
40
|
+
def active_for(resource_owner)
|
|
41
|
+
by_resource_owner(resource_owner).where(revoked_at: nil)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Determines if refresh tokens should be revoked only when the new access token is used,
|
|
45
|
+
# rather than immediately upon refresh. This is based on the presence of the
|
|
46
|
+
# `previous_refresh_token` column in the database.
|
|
47
|
+
#
|
|
48
|
+
# When true (column exists):
|
|
49
|
+
# - Refresh tokens are NOT immediately revoked
|
|
50
|
+
# - New access token stores the old refresh token value in `previous_refresh_token`
|
|
51
|
+
# - Old refresh token is revoked later when the new access token is first used
|
|
52
|
+
# - Multiple concurrent refresh requests can succeed (no database locks)
|
|
53
|
+
# - Better database performance and lower latency
|
|
54
|
+
#
|
|
55
|
+
# When false (column does not exist):
|
|
56
|
+
# - Refresh tokens are immediately revoked using database locks
|
|
57
|
+
# - Only one concurrent refresh request can succeed
|
|
58
|
+
# - May experience database lock contention under high load
|
|
59
|
+
#
|
|
60
|
+
# To enable the revoke-on-use feature and improve performance:
|
|
61
|
+
# rails generate doorkeeper:previous_refresh_token
|
|
62
|
+
# rails db:migrate
|
|
63
|
+
#
|
|
64
|
+
# @return [Boolean] true if previous_refresh_token column exists
|
|
65
|
+
#
|
|
66
|
+
def refresh_token_revoked_on_use?
|
|
67
|
+
column_names.include?("previous_refresh_token")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Returns non-expired and non-revoked access tokens
|
|
71
|
+
def not_expired
|
|
72
|
+
relation = where(revoked_at: nil)
|
|
73
|
+
|
|
74
|
+
if supports_expiration_time_math?
|
|
75
|
+
# have not reached the expiration time or it never expires
|
|
76
|
+
relation.where("#{expiration_time_sql} > ?", Time.now.utc).or(
|
|
77
|
+
relation.where(expires_in: nil),
|
|
78
|
+
)
|
|
79
|
+
else
|
|
80
|
+
::Kernel.warn(::Doorkeeper::Models::ExpirationTimeSqlMath::WARNING_MESSAGE)
|
|
81
|
+
|
|
82
|
+
relation
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def compute_doorkeeper_table_name
|
|
89
|
+
table_name = "oauth_access_token"
|
|
90
|
+
table_name = table_name.pluralize if pluralize_table_names
|
|
91
|
+
"#{table_name_prefix}#{table_name}#{table_name_suffix}"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|