module Doorkeeper::AccessTokenMixin::ClassMethods

Public Instance Methods

authorized_tokens_for(application_id, resource_owner) click to toggle source

Looking for not revoked Access Token records that belongs to specific Application and Resource Owner.

@param application_id [Integer]

ID of the Application model instance

@param resource_owner [ActiveRecord::Base, Integer]

Resource Owner model instance or it's ID

@return [ActiveRecord::Relation]

collection of matching AccessToken objects
# File lib/doorkeeper/models/access_token_mixin.rb, line 277
def authorized_tokens_for(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) click to toggle source

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.

@param previous_refresh_token [#to_s]

previous refresh token value (any object that responds to `#to_s`)

@return [Doorkeeper::AccessToken, nil] AccessToken object or nil

if there is no record with such refresh token
# File lib/doorkeeper/models/access_token_mixin.rb, line 56
def by_previous_refresh_token(previous_refresh_token)
  find_by(refresh_token: previous_refresh_token)
end
by_refresh_token(refresh_token) click to toggle source

Returns an instance of the Doorkeeper::AccessToken with specific token value.

@param refresh_token [#to_s]

refresh token value (any object that responds to `#to_s`)

@return [Doorkeeper::AccessToken, nil] AccessToken object or nil

if there is no record with such refresh token
# File lib/doorkeeper/models/access_token_mixin.rb, line 41
def by_refresh_token(refresh_token)
  find_by_plaintext_token(:refresh_token, refresh_token)
end
by_token(token) click to toggle source

Returns an instance of the Doorkeeper::AccessToken with specific plain text token value.

@param token [#to_s]

Plain text token value (any object that responds to `#to_s`)

@return [Doorkeeper::AccessToken, nil] AccessToken object or nil

if there is no record with such token
# File lib/doorkeeper/models/access_token_mixin.rb, line 28
def by_token(token)
  find_by_plaintext_token(:token, token)
end
create_for(application:, resource_owner:, scopes:, **token_attributes) click to toggle source

Creates a not expired AccessToken record with a matching set of scopes that belongs to specific Application and Resource Owner.

@param application [Doorkeeper::Application]

Application instance

@param resource_owner [ActiveRecord::Base, Integer]

Resource Owner model instance or it's ID

@param scopes [#to_s]

set of scopes (any object that responds to `#to_s`)

@param token_attributes [Hash]

Additional attributes to use when creating a token

@option token_attributes [Integer] :expires_in

token lifetime in seconds

@option token_attributes [Boolean] :use_refresh_token

whether to use the refresh token

@return [Doorkeeper::AccessToken] new access token

# File lib/doorkeeper/models/access_token_mixin.rb, line 253
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

  create!(token_attributes)
end
custom_attributes_match?(token, custom_attributes) click to toggle source

Checks whether the token custom attribute values match the custom attributes from the parameters.

@param token [Doorkeeper::AccessToken]

The access token whose custom attributes are being compared
to the custom_attributes.

@param custom_attributes [Hash]

A hash of the attributes for which we want to determine whether
the token's custom attributes match.

@return [Boolean] true if the token’s custom attribute values

match those in the custom_attributes, or if both are empty/blank.
False otherwise.
# File lib/doorkeeper/models/access_token_mixin.rb, line 186
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) click to toggle source

Extracts the token’s custom attributes (defined by the custom_access_token_attributes config option) from the token’s attributes.

@param attributes [Hash]

A hash of the access token's attributes.

@return [Hash]

A hash containing only the custom access token attributes.
# File lib/doorkeeper/models/access_token_mixin.rb, line 325
def extract_custom_attributes(attributes)
  attributes.with_indifferent_access.slice(
    *Doorkeeper.configuration.custom_access_token_attributes)
end
fallback_secret_strategy() click to toggle source

Determine the fallback storing strategy Unless configured, there will be no fallback

# File lib/doorkeeper/models/access_token_mixin.rb, line 314
def fallback_secret_strategy
  ::Doorkeeper.config.token_secret_fallback_strategy
end
find_access_token_in_batches(relation, **args, &block) click to toggle source

Interface to enumerate access token records in batches in order not to bloat the memory. Could be overloaded in any ORM extension.

# File lib/doorkeeper/models/access_token_mixin.rb, line 103
def find_access_token_in_batches(relation, **args, &block)
  relation.find_in_batches(**args, &block)
end
find_matching_token(relation, application, custom_attributes, scopes) click to toggle source

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.

@param relation [ActiveRecord::Relation]

Access tokens relation

@param application [Doorkeeper::Application]

Application instance

@param scopes [String, Doorkeeper::OAuth::Scopes]

set of scopes

@param custom_attributes [Nilable Hash]

A nil value, or hash with keys corresponding to the custom attributes
configured with the `custom_access_token_attributes` config option.
A nil value will ignore custom attributes.

@return [Doorkeeper::AccessToken, nil] Access Token instance or

nil if matching record was not found
# File lib/doorkeeper/models/access_token_mixin.rb, line 129
def find_matching_token(relation, application, custom_attributes, scopes)
  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)
    end

    matching_tokens.concat(tokens)
  end

  matching_tokens.max_by(&:created_at)
end
find_or_create_for(application:, resource_owner:, scopes:, **token_attributes) click to toggle source

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.

@param application [Doorkeeper::Application]

Application instance

@param resource_owner [ActiveRecord::Base, Integer]

Resource Owner model instance or it's ID

@param scopes [#to_s]

set of scopes (any object that responds to `#to_s`)

@param token_attributes [Hash]

Additional attributes to use when creating a token

@option token_attributes [Integer] :expires_in

token lifetime in seconds

@option token_attributes [Boolean] :use_refresh_token

whether to use the refresh token

@return [Doorkeeper::AccessToken] existing record or a new one

# File lib/doorkeeper/models/access_token_mixin.rb, line 216
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
    custom_attributes = extract_custom_attributes(token_attributes).presence
    access_token = matching_token_for(
      application, resource_owner, scopes, custom_attributes: custom_attributes, include_expired: false)

    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) click to toggle source

Convenience method for backwards-compatibility, return the last matching token for the given Application and Resource Owner.

@param application_id [Integer]

ID of the Application model instance

@param resource_owner [ActiveRecord::Base, Integer]

ID of the Resource Owner model instance

@return [Doorkeeper::AccessToken, nil] matching AccessToken object or

nil if nothing was found
# File lib/doorkeeper/models/access_token_mixin.rb, line 295
def last_authorized_token_for(application_id, resource_owner)
  authorized_tokens_for(application_id, resource_owner)
    .ordered_by(:created_at, :desc)
    .first
end
matching_token_for(application, resource_owner, scopes, custom_attributes: nil, include_expired: true) click to toggle source

Looking for not revoked Access Token with a matching set of scopes that belongs to specific Application and Resource Owner.

@param application [Doorkeeper::Application]

Application instance

@param resource_owner [ActiveRecord::Base, Integer]

Resource Owner model instance or it's ID

@param scopes [String, Doorkeeper::OAuth::Scopes]

set of scopes

@param custom_attributes [Nilable Hash]

A nil value, or hash with keys corresponding to the custom attributes
configured with the `custom_access_token_attributes` config option.
A nil value will ignore custom attributes.

@return [Doorkeeper::AccessToken, nil] Access Token instance or

nil if matching record was not found
# File lib/doorkeeper/models/access_token_mixin.rb, line 94
def matching_token_for(application, resource_owner, scopes, custom_attributes: nil, include_expired: true)
  tokens = authorized_tokens_for(application&.id, resource_owner)
  tokens = tokens.not_expired unless include_expired
  find_matching_token(tokens, application, custom_attributes, scopes)
end
revoke_all_for(application_id, resource_owner, clock = Time) click to toggle source

Revokes AccessToken records that have not been revoked and associated with the specific Application and Resource Owner.

@param application_id [Integer]

ID of the Application

@param resource_owner [ActiveRecord::Base, Integer]

instance of the Resource Owner model or it's ID
# File lib/doorkeeper/models/access_token_mixin.rb, line 68
def revoke_all_for(application_id, resource_owner, clock = Time)
  by_resource_owner(resource_owner)
    .where(
      application_id: application_id,
      revoked_at: nil,
    )
    .update_all(revoked_at: clock.now.utc)
end
scopes_match?(token_scopes, param_scopes, app_scopes) click to toggle source

Checks whether the token scopes match the scopes from the parameters

@param token_scopes [#to_s]

set of scopes (any object that responds to `#to_s`)

@param param_scopes [Doorkeeper::OAuth::Scopes]

scopes from params

@param app_scopes [Doorkeeper::OAuth::Scopes]

Application scopes

@return [Boolean] true if the param scopes match the token scopes,

and all the param scopes are defined in the application (or in the
server configuration if the application doesn't define any scopes),
and false in other cases
# File lib/doorkeeper/models/access_token_mixin.rb, line 161
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() click to toggle source

Determines the secret storing transformer Unless configured otherwise, uses the plain secret strategy

@return [Doorkeeper::SecretStoring::Base]

# File lib/doorkeeper/models/access_token_mixin.rb, line 307
def secret_strategy
  ::Doorkeeper.config.token_secret_strategy
end