Custom Devise 401 unauthorized response
Solution 1
For reference in case anyone else stumbles upon this question when looking for how to customize the json error response when a failed login attempt is made using Devise, the key is to use your own custom FailureApp
implementation. (You can also use this approach to override some redirect behavior.)
class CustomFailureApp < Devise::FailureApp
def respond
if request.format == :json
json_error_response
else
super
end
end
def json_error_response
self.status = 401
self.content_type = "application/json"
self.response_body = [ { message: i18n_message } ].to_json
end
end
and in your devise.rb
, look for the config.warden
section:
config.warden do |manager|
manager.failure_app = CustomFailureApp
end
Some related info:
At first I thought I would have to override Devise::SessionsController, possibly using the recall
option passed to warden.authenticate!
, but as mentioned here, "recall is not invoked for API requests, only for navigational ones. If you want to customise the http status code, you will have better luck doing so at the failure app level."
Also https://github.com/plataformatec/devise/wiki/How-To%3a-Redirect-to-a-specific-page-when-the-user-can-not-be-authenticated shows something very similar for redirection.
Solution 2
If you're simply wanting to change the text displayed with the error message, I believe you can just edit the locale file (/config/locales/devise.en.yml).
The RailsCast on this topic might be helpful too, if you want more specific details. You can find it at http://railscasts.com/episodes/210-customizing-devise
Related videos on Youtube
Matt Huggins
Currently developing with Ruby (Rails) and Javascript (React/Redux). Variety of past & present experience includes mobile development (React Native, Android, Cordova), SQL, Java, PHP, C/C++, etc.
Updated on June 06, 2022Comments
-
Matt Huggins almost 2 years
I'm working on a JSON-based API for my Rails 3.1 app. I'd like to provide a custom failure response instead of the default, which is:
{"error":"You need to sign in or sign up before continuing."}
My API controller includes a before_filter call to
authenticate_user!
, which is what is rendering this JSON response.While searching, I came across this StackOverflow question, which references this Devise wiki entry. Unfortunately, the wiki entry isn't verbose enough for me to understand what it's telling me. Specifically, I have no clue where I'm supposed to put that code such that Devise/Warden knows to render what I want returned.
From the comments on the other SA question, it sounds like I don't need to call
custom_failure!
since I'm using a version of Devise above 1.2 (1.4.2 to be specific). However, the wiki entry doesn't explain where therender
call should go such thatauthenticate_user!
knows to use that instead of its own render call.Where does this
render
call go?Edit: I'm not just trying to change the message itself (a la the devise
en.yml
config); I'm trying to change the actual format of the response. Specifically, I want to return this:render :text => "You must be logged in to do that.", :status => :unauthorized
-
Mateusz over 12 yearsdid you succeed? for ok answer I overwrite SessionController#create method but have no idea where to put this
format.json { render :json => { "status" => ERROR_NOT_REGISTERED }, :status => :unauthorized }
-
-
Matt Huggins over 12 yearsThanks for pointing that out. I've been using a custom error response in that locale file in my project to date, but I'm actually looking to change the format of the response in this situation. I've updated the question to include this.
-
Matt Huggins about 8 yearsI no longer have this question, but based upon the number of votes my question received, your answer will obviously help a lot of people. Thanks for sharing!
-
qix about 8 yearsYes, I'm hoping this will save others some time. :)
-
Doug over 5 yearsThank you for this. Really saved me a lot of time.
-
BenFarhat Souhaib over 4 yearsI tried to keep files organized and created this class under
lib
folder but seems like devise.rb cannot load file from lib -
von spotz almost 3 yearsHello, where to put the FailureApp ? In the Devise wiki the folder lib/devise/failure is mentioned, but it doesn't yet exist in my project structure. Does this require to have some generator run ? Thanks!
-
qix almost 3 years@vonspotz Devise wiki also mentions "If you’re getting an uninitialized constant CustomFailure error, and you’ve put the CustomFailure class under your /lib directory, make sure to autoload your lib files in your application.rb file, like below
config.autoload_paths << Rails.root.join('lib')
" -
von spotz almost 3 years@qix Thanks I already put that path in my autoload_path, but there is no further documentation of FailureApp, how to use it. It seems you can only overwrite the methods and I couldn't even make a call to
redirect_to
in my subclass and my overwritten method. So now I am trying OAuth with theIdentity
gem and other auth methods. It seems more flexible.