17

I've created a rails-api application and proceeded to secure it using token authentication.

I've set a before_filter that is calling a method which uses authenticate_or_request_with_http_token. Everything is working fine but, when the authentication is incorrect i'm getting an html response.

How can I define what format the response should be?

before_filter :restrict_access

private

def restrict_access
  authenticate_or_request_with_http_token do |token, options|
    check_token token
  end
end

def check_token(token)
  Session.exists?(access_token: token)
end
Mohamad
  • 34,731
  • 32
  • 140
  • 219
Federico Nafria
  • 1,397
  • 14
  • 39

2 Answers2

32

By including ActionController::HttpAuthentication::Token::ControllerMethods you include several methods, amongst others request_http_token_authentication which is simply a wrapper around Token.authentication_request. That #authentication_request-method is the culprit and sends the plain text (not HTML as your question suggests) as follows:

def authentication_request(controller, realm)
  controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.gsub(/"/, "")}")
  controller.__send__ :render, :text => "HTTP Token: Access denied.\n", :status => :unauthorized
end

The trick is to override request_http_token_authentication in your ApplicationController to not call Token.authentication_request but to set the correct status and headers and then render JSON instead. Add this to your ApplicationController:

protected
def request_http_token_authentication(realm = "Application")
  self.headers["WWW-Authenticate"] = %(Token realm="#{realm.gsub(/"/, "")}")
  render :json => {:error => "HTTP Token: Access denied."}, :status => :unauthorized
end
BenMorel
  • 34,448
  • 50
  • 182
  • 322
berkes
  • 26,996
  • 27
  • 115
  • 206
  • 5
    Currently, you'll have to define this function as `request_http_token_authentication(realm = 'Application', message = nil)`. – Diego Couto Mar 02 '17 at 15:29
  • With "currently", do you mean Rails edge, @DiegoCouto? – berkes Mar 02 '17 at 16:16
  • 1
    That's right @berkes! [Take a look](https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/http_authentication.rb#L422). Thanks for pointing it out, BTW. :-) – Diego Couto Mar 02 '17 at 16:56
1

From Rails 5, the authenticate_or_request_with_http_token method allows a second parameter with a custom message, so you could just do:

  authenticate_or_request_with_http_token('realm', json_error) do |token, options|
    check_token token
  end
gonzaloriestra
  • 713
  • 6
  • 15