How to remove html redirection in devise authenticate_user
Asked Answered
M

2

22

I use the devise's authenticate_user! method in a controller. This is working fine when the auth_token provided in the request is the correct one but if the authentication fails, I end up with:

curl -XGET 'http://localhost:3000/my_obj?auth_token=wrongtoken'

<html><body>You are being <a href="http://localhost:3000/users/sign_in">redirected</a>.</body></html>

As I use rabl, what is the best way to have something like

{'error' : 'authentication error'}

returned intead of the html redirection ?

Mirianmirielle answered 6/4, 2012 at 9:50 Comment(0)
F
44

I do that in avoid the filter with :format => :json response and do my own filter to render my JSON response if no current_user pass

class MyController < ApplicationController
  before_filter :authenticate_user!, :unless => { request.format == :json }
  before_filter :user_needed, :if => { request.format == :json }

  def user_needed
    unless current_user
      render :json => {'error' => 'authentication error'}, :status => 401
    end
  end
end

An other way, can be cleaner is to define your own FailureApp ( https://github.com/plataformatec/devise/blob/master/lib/devise/failure_app.rb )

class MyFailureApp < Devise::FailureApp
  def respond
    if request.format == :json
      json_failure
    else
      super
    end
  end

  def json_failure
    self.status = 401
    self.content_type = 'application/json'
    self.response_body = "{'error' : 'authentication error'}"
  end
end

In your Devise config file add :

config.warden do |manager| 
  manager.failure_app = MyFailureApp 
end 
Fideicommissum answered 6/4, 2012 at 10:15 Comment(5)
I tried the second approach to avoid having to repeat the same code in all my controllers. I created the class MyFailureApp into lib/failure.rb and change the configuration. I do not manage to have it working though, I get the '!! Unexpected error while processing request: uninitialized constant MyFailureApp'. Any idea why this guy does not get loaded ?Mirianmirielle
You need require your file require 'failure' in your top of Devise configurationFideicommissum
happy it's work to you. I need using this cleanest way in my project too, lolFideicommissum
@Fideicommissum == :json is missing in if request.format == :json(respond method), isn't it?. Otherwise, the json_failure would be rendered in other formats such HTML as well.Nosh
This solution works great! However, when I tested the response body, I kept getting JSON::ParserError exceptions so I ended up setting the response_body as {error: 'Authentication error'}.to_json. Hope this helps someone.Jacobi
H
38

In newer versions of Devise (I'm using 2.2.0), you can use the navigational_formats option in the Devise config file, devise.rb:

# ==> Navigation configuration
# Lists the formats that should be treated as navigational. Formats like
# :html, should redirect to the sign in page when the user does not have
# access, but formats like :xml or :json, should return 401.
#
# If you have any extra navigational formats, like :iphone or :mobile, you
# should add them to the navigational formats lists.
#
# The "*/*" below is required to match Internet Explorer requests.
config.navigational_formats = ["*/*", :html]

So long as :json is not in that list, and your request ends in .json, it will behave as you want.

Hatching answered 11/6, 2013 at 17:30 Comment(4)
This saved me an incredible amount of time, thank you!Kamila
I was baffled all day until I saw your point about the request ending in .json. Good show, good show.Bidding
Add application/json to Headers of the request and you don't need .json at the end f the url.Konstantin
Thank you for this answer, term navigational is quite confusing and makes this comment easy to skip in devise config.Outhe

© 2022 - 2024 — McMap. All rights reserved.