Module: Doorkeeper::AccessTokenMixin::ClassMethods
- Defined in:
- lib/doorkeeper/models/access_token_mixin.rb
Instance Method Summary collapse
-
#authorized_tokens_for(application_id, resource_owner) ⇒ ActiveRecord::Relation
Looking for not revoked Access Token records that belongs to specific Application and Resource Owner.
-
#by_previous_refresh_token(previous_refresh_token) ⇒ Doorkeeper::AccessToken?
Returns an instance of the Doorkeeper::AccessToken found by previous refresh token.
-
#by_refresh_token(refresh_token) ⇒ Doorkeeper::AccessToken?
Returns an instance of the Doorkeeper::AccessToken with specific token value.
-
#by_token(token) ⇒ Doorkeeper::AccessToken?
Returns an instance of the Doorkeeper::AccessToken with specific plain text token value.
-
#create_for(application:, resource_owner:, scopes:, **token_attributes) ⇒ Doorkeeper::AccessToken
Creates a not expired AccessToken record with a matching set of scopes that belongs to specific Application and Resource Owner.
-
#custom_attributes_match?(token, custom_attributes) ⇒ Boolean
Checks whether the token custom attribute values match the custom attributes from the parameters.
-
#extract_custom_attributes(attributes) ⇒ Hash
Extracts the token's custom attributes (defined by the custom_access_token_attributes config option) from the token's attributes.
-
#fallback_secret_strategy ⇒ Object
Determine the fallback storing strategy Unless configured, there will be no fallback.
-
#find_access_token_in_batches(relation, **args, &block) ⇒ Object
Interface to enumerate access token records in batches in order not to bloat the memory.
-
#find_matching_token(relation, application, custom_attributes, scopes, &filter) ⇒ Doorkeeper::AccessToken?
Enumerates AccessToken records in batches to find a matching token.
-
#find_or_create_for(application:, resource_owner:, scopes:, **token_attributes) ⇒ Doorkeeper::AccessToken
Looking for not expired AccessToken record with a matching set of scopes that belongs to specific Application and Resource Owner.
-
#last_authorized_token_for(application_id, resource_owner) ⇒ Doorkeeper::AccessToken?
Convenience method for backwards-compatibility, return the last matching token for the given Application and Resource Owner.
-
#matching_token_for(application, resource_owner, scopes, custom_attributes: nil, include_expired: true) {|token| ... } ⇒ Doorkeeper::AccessToken?
Looking for not revoked Access Token with a matching set of scopes that belongs to specific Application and Resource Owner.
-
#refresh_token_matches?(access_token, token_attributes) ⇒ Boolean
Checks whether a candidate token for reuse matches the refresh token requirement of the current request.
-
#resource_indicators_match?(token, requested_resource) ⇒ Boolean
RFC 8707: checks whether an existing token's audience matches the requested resource indicators.
-
#resource_indicators_supported? ⇒ Boolean
RFC 8707: resource indicators are supported only when the
resourcecolumn exists (added by thedoorkeeper:resource_indicatorsgenerator). -
#revoke_all_for(application_id, resource_owner, clock = Time) ⇒ Object
Revokes AccessToken records that have not been revoked and associated with the specific Application and Resource Owner.
-
#scopes_match?(token_scopes, param_scopes, app_scopes) ⇒ Boolean
Checks whether the token scopes match the scopes from the parameters.
-
#secret_strategy ⇒ Doorkeeper::SecretStoring::Base
Determines the secret storing transformer Unless configured otherwise, uses the plain secret strategy.
Instance Method Details
#authorized_tokens_for(application_id, resource_owner) ⇒ ActiveRecord::Relation
Looking for not revoked Access Token records that belongs to specific Application and Resource Owner.
364 365 366 367 368 369 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 364 def (application_id, resource_owner) by_resource_owner(resource_owner).where( application_id: application_id, revoked_at: nil, ) end |
#by_previous_refresh_token(previous_refresh_token) ⇒ Doorkeeper::AccessToken?
Returns an instance of the Doorkeeper::AccessToken found by previous refresh token. Keep in mind that value of the previous_refresh_token isn't encrypted using secrets strategy.
57 58 59 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 57 def by_previous_refresh_token(previous_refresh_token) find_by(refresh_token: previous_refresh_token) end |
#by_refresh_token(refresh_token) ⇒ Doorkeeper::AccessToken?
Returns an instance of the Doorkeeper::AccessToken with specific token value.
42 43 44 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 42 def by_refresh_token(refresh_token) find_by_plaintext_token(:refresh_token, refresh_token) end |
#by_token(token) ⇒ Doorkeeper::AccessToken?
Returns an instance of the Doorkeeper::AccessToken with specific plain text token value.
29 30 31 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 29 def by_token(token) find_by_plaintext_token(:token, token) end |
#create_for(application:, resource_owner:, scopes:, **token_attributes) ⇒ Doorkeeper::AccessToken
Creates a not expired AccessToken record with a matching set of scopes that belongs to specific Application and Resource Owner.
338 339 340 341 342 343 344 345 346 347 348 349 350 351 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 338 def create_for(application:, resource_owner:, scopes:, **token_attributes) token_attributes[:application] = application token_attributes[:scopes] = scopes.to_s if Doorkeeper.config.polymorphic_resource_owner? token_attributes[:resource_owner] = resource_owner else token_attributes[:resource_owner_id] = resource_owner_id_for(resource_owner) end with_primary_role do create!(token_attributes) end end |
#custom_attributes_match?(token, custom_attributes) ⇒ Boolean
Checks whether the token custom attribute values match the custom attributes from the parameters.
221 222 223 224 225 226 227 228 229 230 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 221 def custom_attributes_match?(token, custom_attributes) return true if custom_attributes.nil? token_attribs = token.custom_attributes return true if token_attribs.blank? && custom_attributes.blank? Doorkeeper.config.custom_access_token_attributes.all? do |attribute| token_attribs[attribute] == custom_attributes[attribute] end end |
#extract_custom_attributes(attributes) ⇒ Hash
Extracts the token's custom attributes (defined by the custom_access_token_attributes config option) from the token's attributes.
412 413 414 415 416 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 412 def extract_custom_attributes(attributes) attributes.with_indifferent_access.slice( *Doorkeeper.configuration.custom_access_token_attributes, ) end |
#fallback_secret_strategy ⇒ Object
Determine the fallback storing strategy Unless configured, there will be no fallback
401 402 403 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 401 def fallback_secret_strategy ::Doorkeeper.config.token_secret_fallback_strategy end |
#find_access_token_in_batches(relation, **args, &block) ⇒ Object
Interface to enumerate access token records in batches in order not to bloat the memory. Could be overloaded in any ORM extension.
110 111 112 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 110 def find_access_token_in_batches(relation, **args, &block) relation.find_in_batches(**args, &block) end |
#find_matching_token(relation, application, custom_attributes, scopes, &filter) ⇒ Doorkeeper::AccessToken?
Enumerates AccessToken records in batches to find a matching token. Batching is required in order not to pollute the memory if Application has huge amount of associated records.
ActiveRecord 5.x - 6.x ignores custom ordering so we can't perform a database sort by created_at, so we need to load all the matching records, sort them and find latest one.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 137 def find_matching_token(relation, application, custom_attributes, scopes, &filter) return nil unless relation matching_tokens = [] batch_size = Doorkeeper.configuration.token_lookup_batch_size find_access_token_in_batches(relation, batch_size: batch_size) do |batch| tokens = batch.select do |token| scopes_match?(token.scopes, scopes, application&.scopes) && custom_attributes_match?(token, custom_attributes) && (filter.nil? || filter.call(token)) end matching_tokens.concat(tokens) end matching_tokens.max_by(&:created_at) end |
#find_or_create_for(application:, resource_owner:, scopes:, **token_attributes) ⇒ Doorkeeper::AccessToken
Looking for not expired AccessToken record with a matching set of scopes that belongs to specific Application and Resource Owner. If it doesn't exists - then creates it.
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 284 def find_or_create_for(application:, resource_owner:, scopes:, **token_attributes) scopes = Doorkeeper::OAuth::Scopes.from_string(scopes) if scopes.is_a?(String) if Doorkeeper.config.reuse_access_token # An empty hash must stay distinct from nil here: nil ignores custom # attributes when matching, while an empty hash only matches tokens # that have no custom attributes set. custom_attributes = extract_custom_attributes(token_attributes) # The refresh token requirement participates in the matching itself # rather than being checked on its single newest result: the newest # matching token may carry the wrong refresh token presence (e.g. it # was issued through a grant with a different `use_refresh_token`) # while an older token satisfies the request and can still be reused. # # RFC 8707: resource indicators must also match so that a token # audience-restricted to one resource is never reused for another. requested_resource = token_attributes[:resource] access_token = matching_token_for( application, resource_owner, scopes, custom_attributes: custom_attributes, include_expired: false, ) do |token| refresh_token_matches?(token, token_attributes) && resource_indicators_match?(token, requested_resource) end return access_token if access_token&.reusable? end create_for( application: application, resource_owner: resource_owner, scopes: scopes, **token_attributes, ) end |
#last_authorized_token_for(application_id, resource_owner) ⇒ Doorkeeper::AccessToken?
Convenience method for backwards-compatibility, return the last matching token for the given Application and Resource Owner.
382 383 384 385 386 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 382 def (application_id, resource_owner) (application_id, resource_owner) .ordered_by(:created_at, :desc) .first end |
#matching_token_for(application, resource_owner, scopes, custom_attributes: nil, include_expired: true) {|token| ... } ⇒ Doorkeeper::AccessToken?
Looking for not revoked Access Token with a matching set of scopes that belongs to specific Application and Resource Owner.
101 102 103 104 105 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 101 def matching_token_for(application, resource_owner, scopes, custom_attributes: nil, include_expired: true, &filter) tokens = (application&.id, resource_owner) tokens = tokens.not_expired unless include_expired find_matching_token(tokens, application, custom_attributes, scopes, &filter) end |
#refresh_token_matches?(access_token, token_attributes) ⇒ Boolean
Checks whether a candidate token for reuse matches the refresh token requirement of the current request.
The candidate's refresh token presence must match what the request asks
for, in both directions. A request that expects a refresh token (e.g. an
authorization_code or password grant while use_refresh_token is
enabled) must not reuse a token issued without one, or the response would
silently omit the refresh token. Conversely, a request that does not ask
for a refresh token must not reuse a token that carries one, or the
response would return a refresh token the request never requested (this
is reachable when refresh_token_enabled is a per-request callable).
When no matching token satisfies the requirement a fresh token is
issued instead.
178 179 180 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 178 def refresh_token_matches?(access_token, token_attributes) access_token.refresh_token.present? == !!token_attributes[:use_refresh_token] end |
#resource_indicators_match?(token, requested_resource) ⇒ Boolean
RFC 8707: checks whether an existing token's audience matches the requested resource indicators. Used during token reuse to prevent returning a token audience-restricted to one resource for a request targeting a different resource.
The comparison runs whenever either side carries a resource, even when no validator is configured: a grant bound to resources still restricts the token's audience (see AuthorizationCodeRequest), so reuse must not silently widen it by matching an unrestricted or differently-scoped token. When both sides are blank the tokens are unrestricted and match.
246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 246 def resource_indicators_match?(token, requested_resource) token_resource = token.try(:resource) # Both blank — neither is audience-restricted, match. return true if token_resource.blank? && requested_resource.blank? # One blank, the other not — mismatch. return false if token_resource.blank? || requested_resource.blank? # Both present — compare as sorted sets. token_resource.split.sort == requested_resource.split.sort end |
#resource_indicators_supported? ⇒ Boolean
RFC 8707: resource indicators are supported only when the
resource column exists (added by the
doorkeeper:resource_indicators generator).
261 262 263 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 261 def resource_indicators_supported? column_names.include?("resource") end |
#revoke_all_for(application_id, resource_owner, clock = Time) ⇒ Object
Revokes AccessToken records that have not been revoked and associated with the specific Application and Resource Owner.
69 70 71 72 73 74 75 76 77 78 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 69 def revoke_all_for(application_id, resource_owner, clock = Time) with_primary_role do by_resource_owner(resource_owner) .where( application_id: application_id, revoked_at: nil, ) .update_all(revoked_at: clock.now.utc) end end |
#scopes_match?(token_scopes, param_scopes, app_scopes) ⇒ Boolean
Checks whether the token scopes match the scopes from the parameters
196 197 198 199 200 201 202 203 204 205 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 196 def scopes_match?(token_scopes, param_scopes, app_scopes) return true if token_scopes.empty? && param_scopes.empty? (token_scopes.sort == param_scopes.sort) && Doorkeeper::OAuth::Helpers::ScopeChecker.valid?( scope_str: param_scopes.to_s, server_scopes: Doorkeeper.config.scopes, app_scopes: app_scopes, ) end |
#secret_strategy ⇒ Doorkeeper::SecretStoring::Base
Determines the secret storing transformer Unless configured otherwise, uses the plain secret strategy
394 395 396 |
# File 'lib/doorkeeper/models/access_token_mixin.rb', line 394 def secret_strategy ::Doorkeeper.config.token_secret_strategy end |