Search code examples
ruby-on-railsoauthruby-on-rails-5doorkeeper

Unable to override custom token error response in doorkeeper


I want to override doorkeeper token error response body method. Currently when I pass username and password wrong at http://localhost:3000/oauth/token url then it will give below error message.

Default doorkeeper response for unauthorised :

{
    "error": "invalid_grant",
    "error_description": "The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."
}

But I want different structure for error message for my API.something like below.

My expected response is :

{
    "status_code": 401,
    "message": "Invalid username or password."
    "result": []
}

I follow official documentation from here and tried below to full fill my expectation.

Tried to make response custom :

Under lib/doorkeeper/oauth/error_response.rb

module Doorkeeper
  module OAuth
    class ErrorResponse
      def body
        {
          "status_code": 401,
          "message": "Invalid username or password."
          "result": []
        }
      end
    end
  end
end

Doorkeeper configuration :

This is doorkeeper.rb file under config -> initializer folder

Doorkeeper.configure do
  ...
  # This block will be called to check whether the resource owner is authenticated or not.
  resource_owner_authenticator do
    fail "Please configure doorkeeper resource_owner_authenticator block located in #{__FILE__}"
  end

  # In this flow, a token is requested in exchange for the resource owner credentials (username and password)
  resource_owner_from_credentials do |routes|
    user = User.find_for_database_authentication(:username => params[:username])
    if user && user.valid_for_authentication? { user.valid_password?(params[:password]) }
      user
    end
  end
  ...
end

But it seems like it is not working. It gives same result as it gives before. It is not going into lib/doorkeeper/oauth/error_response.rb file.

I autoload lib folder in applicatoin.rb file like

module DaihatsuMimamoriApi
  class Application < Rails::Application      
    # config.autoload_paths += %W(\#{config.root}/lib)
    # config.autoload_paths += Dir[Rails.root.join('app', 'lib', '{**/**}')]
    config.autoload_paths += Dir["#{config.root}/lib/**/"]
    # config.autoload_paths << Rails.root.join('lib')
  end
end

Tried many autoload syntax but not get success.


Solution

  • After too many tried I got solution. I don't know is it good way or not but it is working as of now.

    What I done is

    1) Create custom_token_error_response.rb file under lib folder. And then override body method of doorkeeper oauth error module.

    lib/custom_token_error_response.rb

    module CustomTokenErrorResponse
      def body
        {
          status_code: 401,
          message: I18n.t('devise.failure.invalid', authentication_keys: User.authentication_keys.join('/')),
          result: []
        }
        # or merge with existing values by
        # super.merge({key: value})
      end
    end
    

    2) Prepend this module in doorkeeper ErrorResponse module in doorkeepr.rb initializer file.(check last line in below code)

    config/initializer/doorkeeper.rb

    Doorkeeper.configure do
      ...
    
      # In this flow, a token is requested in exchange for the resource owner credentials (username and password)
      resource_owner_from_credentials do |routes|
        user = User.find_for_database_authentication(:username => params[:username])
        if user && user.valid_for_authentication? { user.valid_password?(params[:password]) }
          user
        end
      end
      ...
      #
      # grant_flows %w(authorization_code client_credentials)
      grant_flows %w(password)
    
      # Under some circumstances you might want to have applications auto-approved,
      # so that the user skips the authorization step.
      # For example if dealing with a trusted application.
      # skip_authorization do |resource_owner, client|
      #   client.superapp? or resource_owner.admin?
      # end
      skip_authorization do
        true
      end
    end
    
    Doorkeeper::OAuth::ErrorResponse.send :prepend, CustomTokenErrorResponse
    

    3) Now restart your rails server and you are done.

    You can also refer this blog which I wrote to integrate Rails API + Devise + Doorkeeper. https://scotch.io/@jiggs/rails-api-doorkeeper-devise

    OR

    https://medium.com/@khokhanijignesh29/rails-api-doorkeeper-devise-4212115c9f0d