I'm trying to create an API and for one of my actions I'm restricting it to just admins. To do this, I'm using a before_filter that goes like this:
def authorize_admin!
if !#current_user.admin?
error = { :error => "You must be an admin to do that." }
render params[:format].to_sym => error, :status => 401
end
end
The problem is that when we send back a 401 response, the error is transformed into:
"{\"error\":\"You need to sign in or sign up before continuing.\"}"
This is the response that Devise sends back for when you send a 401 response.
Is there a way that I can turn off this functionality?
What is the Devise version? This wiki page suggests that it should work on 1.2.
https://github.com/plataformatec/devise/wiki/How-To:-Provide-a-custom-failure-response-with-Warden
I think that this page from the Devise wiki can help you.
How To Use HTTP Authentication in Devise
Related
I know this has been asked a lot and I've seen many solutions that don't seem to apply to my issue, so I apologize if I missed something.
When sending a confirmation email using Devise's views with ng-token-auth and devise-token-auth the confirmation email link does not have a redirect_url and will show me an error page when clicking on it, however the confirmation api call succeeds.
in ng-token-auth config I've set:
confirmationSuccessUrl: 'http://localhost:8000/#!/login'
in devise_token_auth.rb I've also set:
config.default_confirm_success_url = 'http://localhost:8000/#!/login'
I have also tried overriding the ConfirmationsController as suggested in many other solutions, all to no avail.
I've got a temporary work around by editing the devise view confirmation_instructions.html.erb by adding redirect_url to the confrimation_url call:
confirmation_url(#resource, redirect_url:'http://localhost:8000/#!/login', confirmation_token: #token)
Thanks in advance, and please let me know if I can provide any additional information.
It seems that the link is always sending an HTTP 406 so the object always has an error, but not always a message. For the time being, I've implemented (an ugly) solution by overriding the confirmations controller as such:
def show
self.resource = resource_class.confirm_by_token(params[:confirmation_token])
yield resource if block_given?
if resource.errors.messages.empty?
set_flash_message!(:notice, :confirmed)
#respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource) }
if signed_in?(resource_name)
#signed_in_root_path(root_path)
redirect_to root_path + '#!/users/' + resource.id.to_s
else
redirect_to root_path + '#!/login'
end
else
respond_with_navigational(resource.errors, status: :unprocessable_entity){ render :new }
end
end
This will, at worst, redirect to the login page. Since the confirmations api call is actually working, the user can sign in. If it fails, I can show a flash message indicating they should try to resend the confirmation email.
If anyone knows of a better solution or what I'm doing wrong to cause the HTTP 406 please let me know!
I have an ExtJS6 app as frontend and rails at backend. Using devise for authentication. sessions#destroy looks like
def destroy
cookies.delete :auth_token
reset_session
sign_out(:user)
render json: { success: true, message: 'Successfully logged out' }
end
it does logs user out (seemingly) but refreshing page logs previous user in
It seems like cookies are not getting reset
Take a look at these two issues:
https://github.com/lynndylanhurley/devise_token_auth/issues/486
https://github.com/lynndylanhurley/devise_token_auth/issues/375
They explain why deleting your session may not be persisting. You'll need to override your sessions controller. Full explanation there.
I'm currently working on a rails Spree application(v 1.3.x).
I have also another application that i have created couple of months ago just suppose myfirstapp.com that is live and so many users have created account on this application so they are registered users now.
So, now i want in my new application which is one i'm currently working on just suppose it is **mysecondapp.com to allow registered users (i.e users who created account on myfirstapp.com) to sign_in in my second app without using my secondapp signup proccess. They should authenticate from myfirstapp.com and get into my secondapp.**
Any way to achieve this?
I think you could use your first app authentication on the seconde one, you can create a link just like facebook or twitter with redirects to a controller and then this controller sends a request to your first app(you will need the security code to do that) and then get the response, save the data you want(user_id or account) and works great...
Api class using protect_from_forgery:
class ApiController < ApplicationController
protect_from_forgery :except => 'custom_login'
end
Doing this you will avoid the default rails protection, with verifies the
<%= hidden_field_tag :authenticity_token, form_authenticity_token -%>
commonly used on rails forms.
Then you can implement your secret hash code, and verifies it like:
class ApiController < ApplicationController
protect_from_forgery :except => :login_from_app2
def login_from_app2
if params[:authentication] == auth_hash
if login(params[:user_credentials])
render :text => "Success" #better you return a json
else
render :text => "Fail login" #better you return a json
end
else
render :text => 'Invalid security hash'
end
end
def auth_hash
"8f2e4b354f193744272fe246ca9e8bf5"
end
end
That way you have a code with only app2 will send on a "post" request to access app1
and you can control the login.
I dont know if this is 100% secure, i think it is, but i will be glad if anyone could explain why this is not a good approach in this case.
I made Devise authentication to log out via GET, but couldn't make it log out using this Angular.js code:
$scope.logout = ->
$http.get('/users/sign_out').success ->
#If it does not redirect from 'editor' to 'login' then you haven't actually logged out
$location.path('editor')
Devise's logout behaviour seems to be random - sometimes it logs out, sometimes not.
And if I enter /users/sign_out into browser's address bar, it logs out always.
Ok, I switched the Devise authentication's log out to POST request to get rid of caching problems and used following Angular.js code:
$scope.logout = ->
$http.post('/users/sign_out').success ->
$location.path('editor')
The first time it logged out fine, as always, but then I couldn't make it to log out.
I decided to make my own method to see what happens:
match '/logout' => 'api#logout', :via => :post
class ApiController < ApplicationController
before_filter :authenticate_user!
def logout
sign_out
if current_user
puts 'Has not signed out!'
else
puts 'Has signed out!'
end
head :ok
end
end
and detected that after sign_out the current_user is always nil, but then the Angular application by some miracle manages to access other methods of ApiController, and current_user isn't nil there!
I do not understand that. Ok, let us suppose that there may follow some other HTTP request, right after (or at the same time as) logout request, passing the authentication cookie and Devise re-logins, but shouldn't the session ID passed in cookie be expired immediately after call of sign_out method?!
sorry I never responded earlier, hope this helps
My Sesisons Controller
$scope.signOutUser = function () {
$http.delete('/api/users/sign_out', {
auth_token: Session.currentUser // just a cookie storing my token from devise token authentication.
}).success( function(result) {
$cookieStore.remove('_pf_session');
$cookieStore.remove('_pf_name');
$cookieStore.remove('_pf_email');
location.reload(true); // I need to refresh the page to update cookies
}).error( function(result) {
console.log(result);
});
}
My Devise Sessions Controller I overrode
class SessionsController < Devise::SessionsController
before_filter :authenticate_user!, only: :destroy
def destroy
token = params[:auth_token]
#user = User.find_by_authentication_token(token)
#user.reset_authentication_token!
sign_out(#user)
render status: :ok, json: {message: "You have successfully logged out"}
end
end
As you can see, I'm not using Rails cookies and thus my answer may not pertain. If I did I would probably add a line like session[:user] = nil in my destroy action.
I am overriding Devise's failure response so that I can set a 401 status code. However, when the user fails to sign in, they are redirected to a page with a "you are being redirected" link. If I remove this :status => 401 from the redirect it works fine.
class CustomFailure < Devise::FailureApp
def redirect_url
new_user_session_url(:subdomain => 'secure')
end
def respond
if http_auth?
http_auth
else
store_location!
flash[:alert] = i18n_message unless flash[:notice]
redirect_to redirect_url, :status => 401
end
end
end
edit
Alternatively I would like to display the flash message and remain on the same page but adding this line of code:
render :text => "unauthorized", :status => 401
causes ruby to complain:
undefined method `render' for #<CustomFailure:0x00000103367f28>
What's happening here?
Proper HTTP statuses for a redirection are in the 30x form (301 and 302 being the most frequently used). By default, the redirect_to helper sets a 302 status header on the HTTP response. If you override that and set that to a 401, your web browser will assume that the response is a regular web page and will render the response body --which, in a redirection, is the boilerplate text "You are being redirected".
As said by #pantulis the browser will display this standard message if the response code is not a 3xx
To workaround this you can perform a javascript redirect:
# example with status 500:
render text: "<script>window.location = '#{url}';</script>", status: 500
This is off-course valid only if you are sure that all your users are using javascript. If your application can be browsed by users that may have disabled javascript you should also include a noscript tag and fallback in the standard "You are being redirected" message
I was actually running into this problem on our QA server, but not locally. It turned out that our memcache was intercepting the message and rendering it as a 200, and causing this message to appear. This was due indirectly to our memcache settings which didn't expect a re-direct from a GET.
From:
$document_root/cache/$uri.html /cache/$uri /cache/$uri.html $uri #memcached
To:
$document_root/cache/$uri.html /cache/$uri /cache/$uri.html $uri #rails
When I have this problem what I have done in the past is something like this:
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
after_filter :check_page_content
...
private
def check_page_content
if response.body.include? "You are being"
html_doc = Nokogiri::HTML(response.body)
uri = html_doc.css('a').map { |link| link['href'] }.first
response.body = "<script>
window.location.replace('#{uri}');
</script>"
end
end
end
What I am doing is checking to see if the page content is "You are being". if this is true I know I am not where I want to be. and I just update the page to where I really want to be with some help of Javascript.
I know its not the most elegant solution but it really does help
Happy Hacking