doorkeeper icon indicating copy to clipboard operation
doorkeeper copied to clipboard

Doorkeeper::AccessToken.find_or_create_for unnecessarily refetches application object

Open stereoscott opened this issue 2 years ago • 1 comments

When creating a new token for an application, despite passing an application object into the method, it's refetched from the database when the token is generated.

Steps to reproduce

Create a token for an existing application:

app = Doorkeeper::Application.find_or_create_by(name: "API")
Doorkeeper::AccessToken.find_or_create_for(
      application: app,
      resource_owner: id,
      scopes: Doorkeeper::OAuth::Scopes.from_array(%w(public))
)

Expected behavior

When passing an object into the find_or_create_for method, it should use that object reference rather than refetching the application object from the DB.

Actual behavior

Here is the create_for(...) method

      def create_for(application:, resource_owner:, scopes:, **token_attributes)
        token_attributes[:application_id] = application&.id
        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

Because :application is used as a keyword argument, it's removed from the token_attributes hash which is passed further down into the create! method. Anytime a new AccessToken is created, generate_token is called which in turn calls attributes_for_token_generator, which passes a full application reference to the token generator.

    # Set of attributes that would be passed to token generator to
    # generate unique token based on them.
    #
    #  @return [Hash] set of attributes
    #
    def attributes_for_token_generator
      {
        resource_owner_id: resource_owner_id,
        scopes: scopes,
        application: application, # see here, this causes a refetch
        expires_in: expires_in,
        created_at: created_at,
      }.tap do |attributes|
        if Doorkeeper.config.polymorphic_resource_owner?
          attributes[:resource_owner] = resource_owner
        end
      end
    end

Because the create! method is only getting passed the application_id and not the full application, when application is called from inside attributes_for_token_generator it causes the application to be fetched from the database again.

This could be avoided by passing the application into token_attributes rather than passing in the id only.

System configuration

Doorkeeper 5.4.0

stereoscott avatar Jun 06 '22 22:06 stereoscott

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Sep 21 '22 04:09 stale[bot]