Devise: Logout *without* redirecting? - ruby-on-rails-3

Im trying to logout with devise but without redirecting! So I want to disable the redirect after logout. The reason for this is I want to render a certain template instead.
How to logout a user from devise without redirecting them for --> this method only <--? Im already overriding the devise session controller.
My method ( called from before filter in application_controller.rb)
def check_concurrent_session
if user_signed_in?
if current_user && !(session[:token] == current_user.login_token)
render :template => "users/logout_duplex", :layout => "application", :locals => { sub_layout => "left" }
# The next line always redirects to signout_path
# I want to disable the complete redirect so it just logs out and shows the rendered template
Devise.sign_out_all_scopes ? sign_out : sign_out(#user)
return
end
end
end

Inherit sessions controller from devise and have your own implementation of after_sign_out_path_for. That's the method devise uses to redirect after signout.
class SessionsController < Devise::SessionsController
protected
def after_sign_out_path_for(resource)
//your implementation
end
end
Add this to your config/routes.rb
devise_for :users, :controllers => {:sessions => 'sessions'}
Devise implementation of the method looks like below
def after_sign_out_path_for(resource_or_scope)
respond_to?(:root_path) ? root_path : "/"
end

Related

Devise after login redirect - double render error

I'm working with rails 3.2 and Devise (the lastest version)
The main idea if the test some variables of the current logged user after sign in. So, for example, if the user has pending creating an address i want to redirect the new address path. But what i get is a double render error.
Here is the code
class ApplicationController < ActionController::Base
protect_from_forgery
# Devise: Where to redirect users once they have logged in
def after_sign_in_path_for(resource)
if current_user.is? :company_owner
if $redis.hget(USER_COMPANY_KEY, current_user.id).nil?
redirect_to new_owner_company_path and return
else
#addr_pending = $redis.hget(PENDING_ADDRESS_KEY,current_user.id)
unless #addr_pending.nil? || !#addr_pending
redirect_to owner_company_addresses_path and return
end
end
end
root_path
end
end
my routes definition
root :to => "home#index"
devise_for :users, :controllers => {
:omniauth_callbacks => "users/omniauth_callbacks"
}
resources :users, :only => :show
namespace :owner do
resource :company do # single resource /owner/company
get 'thanks'
get 'owner' #TODO: esto hay que sacarlo de aquĆ­ y forme parte del login
resources :addresses
end
end
So, when i login with a user with a pedding address creation i get
"render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".
what is wrong with?
redirect_to owner_company_addresses_path and return
So, i just want to redirect to the new address path. I don't understand why i get the error.
Thanks in advance.
---- edit ----
Seems that only one path must be returned (I thought with redirect_to and return was enough, but it does not)
def after_sign_in_path_for(resource)
#final_url = root_path
if current_user.is? :company_owner
if $redis.hget(USER_COMPANY_KEY, current_user.id).nil?
#final_url = new_owner_company_path
else
#addr_pending = $redis.hget(PENDING_ADDRESS_KEY,current_user.id)
unless #addr_pending.nil? || !#addr_pending
#final_url = owner_company_addresses_path
end
end
end
#final_url
end
You should remove redirect_to method call and return statement. after_sign_in_path_for should return only a path:
E.g:
def after_sign_in_path_for(resource)
new_owner_company_path
end

From Rails devise auth to backbone & api?

i want to rebuild an app which is a typical rails 3.2 mvc app into a API + Frontend (Backbone) only. As I have no experience in building APIs in rails including authenticatin:
What's the best way to authenticate with devise using backbone? Using auth_tokens?
How should I make he API? Just printing out JSON or use a gem like Grape?
thanks in advance!
I can explain you the way i do this :
First, i install a standard rails application with devise. After that, i create my own session controller :
class SessionsController < ApplicationController
def authenticate
# this method logs you in and returns you a single_access_token token for authentication.
#user = User.find_for_authentication(:email => params[:user][:email])
if #user && #user.valid_password?(params[:user][:password])
render :json => {:user => {:email => #user.email, :id => #user.id, :firsname => #user.firstname, :lastname => #user.lastname, :team_id => #user.team_id, :singleAccessToken => #user.generate_access_token}}
else
render :json => {:errors => ["Nom d'utilisateur ou mot de passe invalide"]}, :status => 401
end
end
end
As you can see, i send a request to this url with the json looking like :
{
user => {
email => "myemail#toto.com",
password => "monpass"
}
}
And my controller return me the json with user data if every thing is fine, or an error. On json with user, i return an access_token used on next requests to check that the user is allowed to request. I made this filters in my application controller :
class ApplicationController < ActionController::Base
protect_from_forgery
protected
def user_access_token
request.headers["HTTP_X_USER_ACCESS_TOKEN"] || request.headers["HTTP_USER_ACCESS_TOKEN"]
end
def current_user
if token = user_access_token
#user ||= User.find_by_access_token(token)
end
end
def require_user
unless current_user
render :json => {:error => "Invalid Access Token"}, :status => 401
end
end
def require_owner
unless current_user && current_user == object.user
render :json => {:error => "Unauthorized"}
end
end
end
As you can see, on each next request, i will add the access_token in html header on key : HTTP_USER_ACCESS_TOKEN
So, i can check if the user is allowed to make the request.
To make an API, you can use the Rails API gem as see here :
http://railscasts.com/episodes/348-the-rails-api-gem
Good luck.

Rails Devise 2.0 own Log-out action

I use Devise and I want to do my logout action.
What I want to do is, that when I log out, I want to create a own JSON object to return. At this time, after I logt out, I get all my root articles.
How can I write my own destory action like I have found the create action?
class SessionsController < Devise::SessionsController
def create
resource = warden.authenticate!(:scope => resource_name, :recall => :failure)
return sign_in_and_redirect(resource_name, resource)
end
def sign_in_and_redirect(resource_or_scope, resource=nil)
scope = Devise::Mapping.find_scope!(resource_or_scope)
resource ||= resource_or_scope
sign_in(scope, resource) unless warden.user(scope) == resource
return render :json => {:success => true, :redirect => stored_location_for(scope) || after_sign_in_path_for(resource)}
end
def failure
return render:json => {:success => false, :errors => ["Login failed."]}
end
end
And my Routes in routes.rb
devise_for :users, :controllers => {:session => "sessions"} do
get "/users/sing_out" => "devise/sessions#destroy"
end
this is the destroy method of the sessions-controller.
you should be able to customize it to your needs. i think that it would be wiser to add another action and implementing your custom behavior there, as this will be less likely to cause unexpected errors with upgrading devise in the future.
# DELETE /resource/sign_out
def destroy
redirect_path = after_sign_out_path_for(resource_name)
signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
set_flash_message :notice, :signed_out if signed_out
# We actually need to hardcode this as Rails default responder doesn't
# support returning empty response on GET request
respond_to do |format|
format.any(*navigational_formats) { redirect_to redirect_path }
format.all do
method = "to_#{request_format}"
text = {}.respond_to?(method) ? {}.send(method) : ""
render :text => text, :status => :ok
end
end
end

Rails 3 Correctly routing the destroy action for a session

I am refactoring my access_controller into a sessions_controller and can't seem to get my destroy action working properly.
Logging in seems to work fine, but I am unable to log out of a session. Here is the link I have for logging out:
<%= link_to("Logout", :controller => "sessions", :action => 'destroy') %>
routes.rb
resources :sessions
sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
...
end
def destroy
session[:user_id] = nil
flash[:notice] = "You are now logged out"
redirect_to root_url
end
end
When I click "Logout" I get redirected to "/sessions/destroy" with a message of "The action 'show' could not be found for SessionsController". The destroy actions seems to want an id, but I don't need to pass in an id, I just want to run the action.
Ah, I found the answer here: http://railscasts.com/episodes/250-authentication-from-scratch
I need to set up my routes as follows:
get "log_out" => "sessions#destroy", :as => "log_out"
get "log_in" => "sessions#new", :as => "log_in"
resources :sessions

How can I restrict access to ALL devise controllers by IP address'?

How can I restrict access to ALL devise controllers by IP address'? I am trying to allow only users from a specific IP address to view the admin interface / pages.
I found this approach. Which is to include a restrict_access method in the before filter. However, its a bit repetitive if I have to copy this method on all the Devise controllers that I currently use.
Is there a better approach?
class Admin::SessionsController < Devise::SessionsController
before_filter :restrict_access
# Needed to restrict access to a set of IP's only. We don't want random users trying to access the admin interface
def restrict_access
if Rails.env == 'development' or Rails.env == 'test'
whitelist = ['59.120.201.20', '59.120.201.21'].freeze
else
whitelist = ['59.120.201.20', '59.120.201.21'].freeze
end
unless whitelist.include? request.remote_ip
redirect_to root_path, :notice => 'Access denied!'
end
end
...
Build a class like the following and place it in RAILS_ROOT/lib/blacklist_constraint.rb.
class BlacklistConstraint
def initialize
if Rails.env == 'development' or Rails.env == 'test'
#whitelist = ['59.120.201.20', '59.120.201.21'].freeze
else
#whitelist = ['59.120.201.20', '59.120.201.21'].freeze
end
end
def matches?(request)
!#whitelist.include?(request.remote_ip)
end
end
... and in your routes.rb file...
match "*", :constraints => BlacklistConstraint.new, :controller => "blacklist", :action => "my_access_denied_action"
You may need to load the class in an initializer, or modify your config.autoload_paths += %W(#{Rails.root}/lib) in config/application.rb (Rails3.x).
I believe all of the Devise Controller extend your Application controller, so you could put the method in the ApplicationController as a protected method then you only need to call the
before_filter :restrict_access
on each devise controller.