I'm using devise for the first time in a project so probably this is gonna be a newbie question, I would like to know how to make a user rememberable after signup. Currently I'm logging in the user after signup with something like this:
class UsersController < Devise::RegistrationsController
respond_to :html, :json, :js
def create
if request.format.js? || request.format.json?
build_resource
resource.save
sign_in :user, resource if resource.valid? && request.format.js?
respond_with resource
else
super
end
end
end
Note I have my own version of create since I need to respond to javascript and json requests. Any help would be appreciated.
Thanks in advance!.
I haven't actually tried this yet but you should be able to call resource.remember_me! and that should take care of creating the token and saving it for the given resource.
Related
I am trying to make an API for a project. I followed instructions from this video - https://www.youtube.com/watch?v=lgdUqtw4weg&t=165s
Basically in the video, I make a token column for the user model. And set up the controller so I can use a post method.
But when i run the POST.
I get the error saying
{"error":"You need to sign in or sign up before continuing."}
I think Devise is interfering with the POST and sees that the user has not logged in when trying to visit non public pages.
How do I get past this and get the token ?
Here is my api controller.
class Api::V1::UserSerializedController < ApplicationController
protect_from_forgery except: :index
respond_to :json
def create
user = User.where(email: params[:email]).first
if user.valid_password?(params[:encrypted_password])
render json: user.as_json(only: [:email, :authentication_token]),status: :created
else
head(:unauthorized)
end
end
def show
end
end
You are inheriting ApplicationController and i'm guessing you have authenticate_user! before action set there.
You'd have to either change the parent class to something like ApiController (i'd prefer this)
OR
skip this action for your Api::V1::UserSerializedController
In my Rails project I am using Devise and I've turned on allow_unconfirmed_access in the devise initializer like this:
config.allow_unconfirmed_access_for = 1.day
This works great. If a user has not confirmed their account via the confirmation email, they can still login for 24 hours. After this, if they attempt to login, Devise handles it and gives a flash message and returns a 401, disallowing the login.
I want to be able to hook into this and add a step to auto-resend the confirmation email but I can't figure out for the life of me where to do it.
You can extend the Devise::SessionsController to add this behavior:
class Users::SessionsController < Devise::SessionsController
before_action :resend_confirmation_email_if_unsent, on: :create
def resend_confirmation_email
#user = resource # resource is a devise controller helper method
unless #user.active_for_authentication?
#user.resend_confirmation_instructions # Resets token
# Or, if you don't want to reset the tokn
# #user.send_confirmation_instructions
end
# ....
end
I know this is an old question, but I thought I might as well answer it as I was dealing with the same situation.
Anthony E's answer is almost correct, but it missed the fact that resource is not defined before the create action starts, thus resource is nil at that moment. My solution was this:
class Users::SessionsController < Devise::SessionsController
before_action :resend_confirmation_email_if_needed, on: :create
def resend_confirmation_email_if_needed
#user = resource_class.find_by_email(resource_params[:email])
unless #user.nil? || #user.active_for_authentication?
#user.resend_confirmation_instructions
end
end
end
I'm not sure if it's a good idea to retrieve the user this way. It would be much easier if super do |resource| worked for this, but it only runs upon successful login, which is not the case.
Hope this helps!
Spree 2.3 with spree_auth_devise, Rails 4.0
I am trying to redirect users after sign in based on their role. The best solution would be to modify a path, a la Devise, in an initializer, but these don't seem to exist for Spree. The next best solution would be to create a decorator on the sessions controller, but I can't find this controller, nor can I access it when I attempt to follow the information from rake routes
How can I redirect users to a new location after login, in a Spree app, based on their role?
UPDATE
Overwriting the after_sign_in_path_for(resource) method results in the method being triggered, but still rerouting to the admin_path.
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
byebug # this is triggered
root_path
end
end
### OR ####
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
byebug # this is triggered
if spree_current_user.has_spree_role?("admin")
admin_path
elsif spree_current_user.has_spree_role?("designer")
new_designers_spree_variant_path
else
root_path
end
end
end
My attempts are being documented here:
https://gist.github.com/asteel1981/0f258260974f4d748fb5
Thanks in advance.
Thanks to #mvidaurre for spending some time with me drilling down into this.
The issue is that spree_auth_devise has a second method that attempts to reroute to the last page attempted, meaning I needed to modify not only the after_sign_in_path_for method, but the Spree method redirect_back_or_default(default).
Additionally, because my user signs in through the admin/sign_in route, I needed to access the Spree::Admin::UserSessionsController instead of simply the Spree::UserSessionsController.
My early solution looks like this:
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
if spree_current_user.has_spree_role?("admin")
admin_path
elsif spree_current_user.has_spree_role?("designer")
'/designers/spree_variants/new' #rails helper gives the wrong path, not sure why
else
root_path
end
end
end
# app/controllers/spree/admin/user_sessions_controller.rb
Spree::Admin::UserSessionsController.class_eval do
def redirect_back_or_default(default)
if spree_current_user && spree_current_user.has_spree_role?("admin")
redirect_to(session["spree_user_return_to"] || default)
session["spree_user_return_to"] = nil
else
redirect_to(default)
end
end
end
The relevant spree source code is here:
https://github.com/spree/spree_auth_devise/blob/8cb2d325b2c1da02cbe137404d8dda89cc1613a2/lib/controllers/backend/spree/admin/user_sessions_controller.rb
Thanks for your answer in the comments. spree_auth_devise defines the Spree::UserSessionsController you can decorate the controller and use the method described in: How To: redirect to a specific page on successful sign in
You can implement your custom method for:
def after_sign_in_path_for(resource)
current_user_path
end
Maybe something like this:
def after_sign_in_path_for(resource)
if spree_current_user.has_spree_role?("designer")
root_path
elsif spree_current_user.has_spree_role?("admin")
admin_path
else
root_path
end
end
Spree documentation seems to cover this, check out:
http://guides.spreecommerce.com/developer/authentication.html
It seems to be a good starting point for what you're trying to achieve.
I have extended the devise controller for sessions. To add some extra functionality when a user logs in. Now upon login, IF no username or password are entered I get error :
SessionsController#create
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
I searched last week for up to 2 hours how to fix this anyone can help me out on this one? Would be highly appreciated
How to correctly add my custom functionality but still preserve the Devise create action?
class SessionsController < Devise::SessionsController
def create
#user = User.where(:id => current_user.id).first
#moderated = Asset.where(:attachable_id => current_user.id, :moderated => true).first
if #user.sign_in_count.to_i == 1
def after_sign_in_path_for(resource)
"/welcome/basics"
end
else
if #moderated.nil?
unless #user.has_photo?
def after_sign_in_path_for(resource)
"/home/no_photo"
end
end
else
def after_sign_in_path_for(resource)
"/home/moderated"
end
end
end
end
end
If your additional functionality consists of redirecting the user to a different page at his first login i would suggest defining after_sign_in_path_for in the application controller as suggested by the wiki.
class ApplicationController < ActionController::Base
private
def after_sign_in_path_for(resource)
if current_user.sign_in_count == 1
"/welcome/basics"
else
"/other/path"
end
end
end
Please note that this only works if User is the only resource that can sign into your application. Otherwise you would have to differentiate in this method as well via the resource parameter.
With the Confirmable module enabled, Devise will not allow an unconfirmed user to sign in after a predefined period of time has elapsed. Instead the user is redirected back to the sign in page with the flash message "You have to confirm your account before continuing".
This is an undesirable interaction model, as a flash notice does not provide adequate space to properly explain to the user why access has been denied, what "confirm your account" means, provide a link to resend the confirmation, and instructions on how to check your spam folder and so on.
Is there a way I can change this behaviour to redirect to a specific URL instead?
Sorry at first I thought you meant after Sign Up not Sign In. So the down below works for how to direct users after Sign Up and what you need to do for Sign In is to create a custom Devise::FailureApp
See the wiki page: https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-when-the-user-can-not-be-authenticated
Then within your custom FailureApp overwrite redirect_url method from https://github.com/plataformatec/devise/blob/master/lib/devise/failure_app.rb:
def redirect_url
if warden_message == :unconfirmed
custom_redirect_path
else
super
end
end
For custom redirect after Sign Up:
There is a controller method after_inactive_sign_up_path_for within the RegistrationsController that you can overwrite to accomplish this.
First in your Routes you will need to specify to use your custom controller:
config/routes.rb:
devise_for :users, :controllers => { :registrations => "users/registrations" }
Second you create your custom controller that inherits from the normal controller in order to overwrite the method:
app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
protected
def after_inactive_sign_up_path_for(resource)
signed_up_path
end
end
In this case for my App my Devise model is User so you may want to change that namespace if your model is named differently. I wanted my users to be redirected to the signed_up_path, but you can change that to your desired path.
I just did this, but took a different approach.
in app/controllers/sessions_controller.rb:
class SessionsController < Devise::SessionsController
before_filter :check_user_confirmation, only: :create
#
# other code here not relevant to the example
#
private
def check_user_confirmation
user = User.find_by_email(params[:email])
redirect_to new_confirmation_path(:user) unless user && user.confirmed?
end
end
This worked for me and seemed minimally invasive. In my app new sessions always have to go through sessions#create and users always sign in with their email address, so this may be a simpler case than yours.
You can of course redirect_to any location you desire in the check_user_confirmation method. new_confirmation_path was the logical choice for me because it provides users with the resources to get confirmed.
This is my solution you need to add :unconfirmed message on devise locales below the sessions.
in app/controllers/sessions_controller.rb
def check_user_confirmation
user = User.where(email: params[:user][:email]).take
unless user && user.confirmed?
set_flash_message! :alert, :unconfirmed
expire_data_after_sign_in!
respond_with user, location: after_inactive_sign_up_path_for(user)
end
end
protected
def after_inactive_sign_up_path_for(resource)
new_user_session_path
end