I need to override Devise sessions controller during the login process (Rails 3.0.9, Ruby 1.9.2, Devise 1.3.4), I tried this without any effect
class SessionsController < Devise::SessionsController
# GET /resource/sign_in
def new
resource = build_resource
clean_up_passwords(resource)
respond_with_navigational(resource, stub_options(resource)){ render_with_scope :new }
end
end
Ideas?
EDIT
As indicated in the answer, I also need to change the route. In addition, I also need to copy the views. It's better explained here
http://presentations.royvandewater.com/authentication-with-devise.html#8
My custom strategy:
devise.rb
config.warden do |manager|
manager.strategies.add(:custom_strategy) do
def authenticate!
... authenticate against 3rd party API...
if res.body =~ /success/
u = User.find_or_initialize_by_email(params[:user][:email])
if u.new_record?
u.save
end
success!(u)
end
end
end
Have you altered your route to use your new controller?
/config/routes.rb
devise_for :users, :controllers => {:sessions => "sessions"}
Related
I've tried the code on Devise's github. In my application controller, i have:
after_filter :store_location
def store_location
# store last url - this is needed for post-login redirect to whatever the user last visited.
if (request.fullpath != "/users/sign_in" &&
request.fullpath != "/users/sign_up" &&
request.fullpath != "/users/password" )
session[:previous_url] = request.fullpath
puts 'stores location'
end
end
def after_update_path_for(resource)
session[:previous_url] || dashboard_path
puts 'after update'
end
When I check my server, the puts statement from the store_location method appears, but the puts statement from after_update_path_for does not. How do I get the after_update_redirect to work?
Here is what devise says to do, but it isn't working:
https://github.com/plataformatec/devise/wiki/How-To:-Redirect-back-to-current-page-after-sign-in,-sign-out,-sign-up,-update
From the documentation:
(Object) after_update_path_for(resource) (protected)
The default url to be used after updating a resource. You need to overwrite this method in your own RegistrationsController.
So creating your ow RegistrationsController is correct. Here is a simpler solution though:
after_update_path_for calls signed_in_root_path(resource) which looks a home #{scope}_root_path. Scope here is often user (but if not you probably know what it is). In the case of 'user', implementing user_root_path in your application controller, returning your dashboard_url, should work.
def user_root_path
dashboard_url
end
Although it seemed a bit hackish to me at first, I believe it is quite 'ok'; the root path for the user-scope is could indeed be the dashboard page.
Here's how i solved the problem:
class RegistrationsController < Devise::RegistrationsController
protected
def after_update_path_for(resource)
puts 'this is happening yoyo mama'
flash[:notice] = "Account succesfully updated"
edit_user_registration_path
end
end
routes:
devise_for :users, :controllers => { :registrations => :registrations }
The only problem is that this will only do the redirect if changing the password is successful. If not, the redirect does not happen. Does anyone know how to make it so the redirect will also happen if there are errors?
As per the Devise docs, override the default and add the route. There's no need to set the flash message unless of course you want to change that as well.
# Example subclass/override (registrations_controller.rb)
class Users::RegistrationsController < Devise::RegistrationsController
protected
def after_update_path_for(resource)
user_path(resource)
end
end
# Example routing config (in routes.rb):
devise_for :users, :controllers => { :registrations => :registrations }
respond_with resource, :location => after_update_path_for(resource) is the code which set the redirection path after update. To change the default redirect, override following method in your application controller by adding the following code
def after_update_path_for(resource_or_scope)
dashboard_url
end
Overriding the route in the ApplicationController also did not work for me, but adding it to the Users::RegistrationsController worked.
For example,
class Users::RegistrationsController < Devise::RegistrationsController
def after_update_path_for(resource)
current_user
end
on a related note, the after_sign_in_path can be added to the SessionsController
class Users::SessionsController < Devise::SessionsController
def after_sign_in_path_for(resource)
current_user
end
my routes look like this:
devise_for :users, controllers: {
confirmations: "users/confirmations",
passwords: "users/passwords",
registrations: "users/registrations",
sessions: "users/sessions",
unlocks: "users/unlocks",
}
If someone could shed some light It would be greatly appreciated as I'm bit clueless on this issue. Basically I'm trying to get the devise authentication done with a JSON only rails rest api app built on top of rails-api gem.
I've already implemented the Sessions and Registrations handling as described below.
class ApplicationController < ActionController::API
include ActionController::MimeResponds
end
sessions_controller.rb
class SessionsController < Devise::SessionsController
prepend_before_filter :require_no_authentication, :only => [:create ]
before_filter :ensure_params_exist
def create
build_resource
resource = User.find_for_database_authentication(:email => params[:user][:email])
return invalid_login_attempt unless resource
if resource.valid_password?(params[:user][:password])
sign_in("user", resource)
render :json=> {:success=>true, :auth_token=>resource.authentication_token, :email=>resource.email}
return
end
invalid_login_attempt
end
def destroy
sign_out(resource_name)
end
protected
def ensure_params_exist
return unless params[:user][:email].blank?
render :json=>{:success=>false, :message=>"missing login email parameter"}, :status=>422
end
def invalid_login_attempt
warden.custom_failure!
render :json=> {:success=>false, :message=>"Error with your login or password"}, :status=>401
end
end
registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
skip_before_filter :verify_authenticity_token, :only => :create
def create
user = User.new(params[:user])
if user.save
render :json=> {:user => [:email => user.email, :auth_token => user.authentication_token]}, :status => 201
return
else
warden.custom_failure!
render :json=> user.errors, :status=>422
end
end
end
Everything works fine so far. In my other controllers I've added this line to secure them. before_filter :authenticate_user! but I get this error when calling with auth_token. ?auth_token=xxxxxxxxxxx
undefined method authenticate_user!
controllers
class RestaurantsController < ApplicationController
before_filter :authenticate_user!
def index
#restaurants = Restaurant.all
end
def show
#restaurant = Restaurant.find(params[:id])
end
end
Still not sure what might be the issue here - I'm assuming this is happening because something is missing to work devise properly as its using ActionController::API
UPDATE
It seems the issue is with my routes.rb itself - This is how the routes are done.
require 'api_constraints'
MyApi::Application.routes.draw do
namespace :api, defaults: {format: 'json'} do
scope module: :v1, constraints: ApiConstraints.new(version: 1,default: true) do
devise_for :users
resources :friends
resources :locations
resources :profiles
resources :users
end
scope module: :v2, constraints: ApiConstraints.new(version: 2) do
# Future releases of the API can go here
end
end
end
Now If repeat the devise_for :users outside the scope everything started working.
require 'api_constraints'
MyApi::Application.routes.draw do
namespace :api, defaults: {format: 'json'} do
scope module: :v1, constraints: ApiConstraints.new(version: 1,default: true) do
devise_for :users
resources :friends
resources :locations
resources :profiles
resources :users
end
scope module: :v2, constraints: ApiConstraints.new(version: 2) do
# Future releases of the API can go here
end
end
devise_for :users
end
Does any one has an explanation why?
Step 1. Override devise controllers with custom controllers that replace redirects with JSON responses. Here's a custom SessionController that uses JSON responses .
Step 2. If an authentication error occurs, the control goes to warden which uses a failure_app which is nothing but a Rack application which sets the flash messages and renders/redirects based on the requested format. You need a custom json response with an errors key holding an array of errors instead of the default error key. So you need a custom_auth_failure_app.rb under config/initializers with this content.
Step 4. Now we have to tell Devise to use the custom failure app by adding this to config/initializers/devise.rb :
config.warden do |manager|
manager.failure_app = CustomAuthFailure
end
Step 5. Enable token_authenticatable in the devise model and add the following to config/initializers/devise.rb
config.http_authenticatable = true
config.skip_session_storage = [:http_auth, :token_auth]
Step 6. If you are using a true JSON API which doesn't use session, use a version of warden ( > 1.2.2 ) which has patches to handle a nil session.
Also read my blog post about creating a tested, documented and versioned JSON API using Rails4 + Rails-API + Devise which talks about all these steps.
For anyone having this same issue where the devise helper methods like authenticate_user are not working when using devise_token_auth and rails-api, try adding this line to app/controllers/application_controller.rb:
include DeviseTokenAuth::Concerns::SetUserByToken
When you run the devise_token_auth installation, it is supposed to automatically create this concern in the ApplicationController. This concern gives access to the helper methods like authenticate_user. Problem is, that concern doesn't get added if you're using rails-api instead of vanilla rails. So, you have to add it manually.
Not sure the root cause, but I suspect its because rails-api's ApplicationController inherits from ActionController::API, which is different from vanilla rails' ApplicationController, which inherits from ActionController::Base.
Using Devise to manage users sessions / registrations I would need to perform specific tasks (updating some fields in the users table for this specific user for example) each time a user signs in, and before he gets redirected by devise to the home page for connected users.
Do I have to override devise SessionsController, and if yes, how?
Alternatively, you can create your own sessions controller
class SessionsController < Devise::SessionsController
def new
super
end
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
if !session[:return_to].blank?
redirect_to session[:return_to]
session[:return_to] = nil
else
respond_with resource, :location => after_sign_in_path_for(resource)
end
end
end
And in routes.rb add:
devise_for :users, controllers: {sessions: "sessions"}
If you look at Devise's implementation of sessions_controller#create, you'll notice that they yield if you pass a block.
So, just subclass their sessions controllers and pass a block when you call super. To do that, first tell Devise in routes.rb that you'd like to use your own sessions controller:
devise_for :users, controllers: { sessions: 'users/sessions' }
And then create a SessionsController class and pass a block when you call super in your create method. It would look something like this:
# app/controllers/users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
layout "application"
# POST /login
def create
super do |user|
if user.persisted?
user.update(foo: :bar)
end
end
end
end
Most of the Devise controller methods accept a block, so you could do this for registration, forgot password, etc as well.
Devise provides after_database_authentication callback method.You have full access for the current authenticated user object over there.
If you want to update current user name after every successful login you can do that like below.
class User < ActiveRecord::Base
devise :database_authenticatable
def after_database_authentication
self.update_attributes(:name => "your name goes here")
end
end
configure devise for using your controller changing config/routes.rb devise_for :users, controllers: { ... , sessions: "sessions", ... }
create a app/controllers/sessions_controller.rb or generate it using rails g devise:controllers users -c=sessions
prepend_before_action
class SessionsController < Devise::SessionsController
prepend_before_action :your_task, only: [:create] # Change this to be any actions you want to protect.
private
def your_task
return if your_task_is_OK # if your task is good, return to the super method otherwise render the new
self.resource = resource_class.new sign_in_params
respond_with_navigational(resource) do
flash.now[:alert] = "your notice message"
render :new
end
end
end
i have a rails 3.2.8 application that uses devise 2.0.0 for authentication i integrated refinerycms with this application it is integrated successfully now problem is when user hit
localhost/refinery it takes me to refinery login page but after
successfull login it redirects back to my application home page rather
than refinerycms dashboard page.
So i want to know how to redirect to the
refinery dashboard after refinery successfull login procedure.
I have route mount Refinery::Core::Engine,
:at => '/' root to:'projects#index'
etc
Create a custom devise controller that redirects the user to the place you want once they are logged in:
Create the Custom Controller: controllers/users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
before_filter :authenticate_user!, :only => [:destroy]
def new
super
end
def create
#Can edit this for custom redirect
super
end
def destroy
super
end
protected
def stub_options(resource)
methods = resource_class.authentication_keys.dup
methods = methods.keys if methods.is_a?(Hash)
methods << :password if resource.respond_to?(:password)
{ :methods => methods, :only => [:password] }
end
def after_sign_in_path_for(resource)
#Can edit this for custom redirect
super
end
def after_sign_out_path_for(resource)
super
end
private
end
then tell devise to use that controller in your routes.rb:
devise_for :users, :controllers => {:sessions => "users/sessions"}
I am using rails3 and gem devise and i have two roles admin and customer and i want after user
sign out admin should redirect to different path and customer should redirect to different path
when sign out..
You can get your desired functionality by using devise method after sign_out path.
but before you define these methods in application helper.
def is_admin?(user)
admin_role = Role.find(:first, :conditions => ["name = ?", "admin"])
return user.roles.include?(admin_role)
end
def is_customer?(user)
admin_role = Role.find(:first, :conditions => ["name = ?", "customer"])
return user.roles.include?(admin_role)
end
After that include application helper in application controller and define this method
def after_sign_out_path_for(resource_or_scope)
if is_admin?(current_user)
home_path = "/admin/users/sign_in"
elsif is_customer?(current_user)
home_path = "/customer"
end
respond_to?(home_path, true) ? send(root_path) : home_path
end
Hope it will work fine !!!
You can override the after_sign_out_path_for(resource) method that devise uses. Just mention the logic or just the desired redirection path after checking the roles of your user in the method. The devise session's destroy action calls the redirection path through this method.
def after_sign_out_path_for(resource)
#logic
end
Hope this is helpful..
The Devise README covers how to set up custom routes and controllers. In short you'll want to customize your routes for each model, e.g.:
devise_for :admins, :controllers => { :sessions => "admins/sessions" }
devise_for :customers, :controllers => { :sessions => "customers/sessions" }
Then create the corresponding controllers and override Devise::SessionsController#destroy, e.g.:
class Admins::SessionsController < Devise::SessionsController
def destroy
super
redirect_to ...
end
end