I am developing an application using these three gem I wrote in the title of this post. I set up devise with the confirmable module(?), so when a user creates an account with its email/password, it receives a confirmation email. If the user sign up with facebook (using omniauth-facebook gem) devise skips the confirmation step.
In user.rb
"Of course the :confirmable is active in the model"
...
# Omniauth-facebook
def self.find_for_facebook_oauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.first_name = auth.info.first_name
user.last_name = auth.info.last_name
user.skip_confirmation!
# user.image = auth.info.image # assuming the user model has an image
end
end
...
The thing comes when I added the wicked gem for the wizard. I configured the routes file
in routes.rb
MyApp::Application.routes.draw do
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks",
:registrations => "registrations" }
root 'home#index'
# Registration wizard routes
resources :after_register
end
The I created a registration_controller to override the devise registration methods
class RegistrationsController < Devise::RegistrationsController
def create
super
end
protected
def after_sign_in_path_for(resource)
puts "<<<<<<<<<<<<<<<< SIGN IN"
after_register_path(:import_contacts)
end
def after_sign_up_path_for(resource)
puts "<<<<<<<<<<<<<<<< SIGN UP ACTIVE"
after_register_path(:import_contacts)
end
def after_inactive_sign_up_path_for(resource)
puts "<<<<<<<<<<<<<<<< SIGN IN INACTIVE"
after_register_path(:import_contacts)
end
end
And then, I created a new controller to handle the steps of the wizard with wicked.
class AfterRegisterController < ApplicationController
include Wicked::Wizard
before_filter :authenticate_user!
steps :import_contacts, :select_agents, :wish_form
def show
#user = current_user
render_wizard
end
def update
#user = current_user
#user.attributes = params[:user]
render_wizard #user
end
end
When I create a user rith email/password, the wizard comes and everything works fine, but when I try to sign up with facebook, the wizars never comes.
Any hint???
Thank you!!
If everything is configured in the usual way (and what I can see looks pretty standard), then signing in using Facebook won't go via the RegistrationsController at all. It will go via the OmniauthCallbacks controller which you haven't posted the code for. It depends what you do in there when they log in via Facebook. I assume that you have a facebook() method which calls User.find_for_facebook_oauth(auth). Are you then just signing them in rather than going to the RegistrationsController? If so, then the RegistrationsController won't be touched, so its overridden after_sign_in_path_for won't have any effect.
If you want to have your overridden after_sign_in_path_for take effect throughout your app, you can define it in your ApplicationController. If you only want it to take effect in your OmniauthCallbacksController, you could define it there. In either case, they'll be hitting it every time they log in using Facebook (and if you put it in the ApplicationController, every time anyone logs in using any method), so you'd need to keep track of the fact that they have already been through the wizard, assuming you want to make sure that it only happens the first time they sign in. If you're using the devise :trackable module, perhaps checking the user.sign_in_count would be appropriate, or perhaps you have some other way to easily check if they have been through the wizard already.
UPDATE FOR COMMENT QUESTIONS:
Your first question: "Assuming I put the after_sign_in_path_fot in ApplicationController, I should remove the after_inactive_sign_up_path_for method in RegistrationsController, right?" It depends on what behaviour you want. With the RegistrationsController as in your question it will go to the wizard after they've signed up and before they've confirmed their email (because of after_inactive_sign_up_path_for which will be called in this case). When they confirm and sign in, the after_sign_in_path_for in ApplicationController will send them to the wizard again. So yes, remove inactive from RegistrationsController if you just want wizard after sign in. Then probably after_sign_up_path_for(resource) in RegistrationsController is unnecessary because the default implementation in devise just calls after_sign_in_path_for(resource) which you will have in your ApplicationController. Anyway, after_sign_up_path_for() won't be called if you always require confirmation because of the logic in the default implementation of RegistrationsController.create() - requiring confirmation will result in resource.active_for_authentication? returning false which causes after_inactive_sign_up_path_for(resource) to be called.
For the question in your second comment, you said "If I remove the after_sign_in_path_for in ApplicationController" - I assume you meant RegistrationsController? If that's right, then yes, there would be no methods needed in your overridden version of RegistrationsController (if that's the whole controller you pasted in your question) because your create() just calls super, the after_sign_in_path_for would be in ApplicationController and you probably don't want either of the after_(inactive)_sign_up_path_for methods as discussed above. So yes, there would be no need for your RegistrationsController. You could remove it completely and remove the :registrations => "registrations" in routes.rb - the devise implementation of RegistrationsController will then be used again.
Then you say "just override the methods of RegistrationsController in ApplicationController?". The only method you will have left from your RegistrationsController is the after_sign_in_path_for(resource) in ApplicationController, so I don't think there will be any other methods from your RegistrationsController that you need in ApplicationController. Let me know if I've missed one of your requirements or made an incorrect assumption.
Related
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!
I presume there's some value-add from using the Devise Registrations controller but I haven't yet managed to figure it out.
Other than signing the user in after they're created, why would you use the Devise Registrations controller rather than simply having
class UsersController < ApplicationController
...
def new
end
def create
#user = User.create params[:user]
sign_in :user, #user
redirect_to... # whatever's next
end
end
What does the core Devise Registrations controller do that the Users controller doesn't?
Take a look for yourself
https://github.com/plataformatec/devise/blob/master/app/controllers/devise/registrations_controller.rb
If you think it's not worth it, then don't use it.
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
I'm using devise and have a quick question. How can I redirect the :authenticate_user! before_filter to the user sign up page instead of sign in? I've been going through https://github.com/plataformatec/devise/blob/master/lib/devise/controllers/helpers.rb but haven't had much luck figuring out a solution.
I had a similar issue where I needed to redirect to the signup if the user was not logged in.
I fixed it by adding a method to the application_controller.rb and using it as a before filter in the other controllers.
Keep in mind that is is more of a temporary solution because it skips a bunch of deviseĀ“s abstractions.
before_filter :auth_user
def auth_user
redirect_to new_user_registration_url unless user_signed_in?
end
You're going to have to create a custom FailureApp that inherits from Devise's FailureApp as seen here: https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-when-the-user-can-not-be-authenticated
I added a wiki page showing the correct way to do this with a failure app (as Steven initially hinted at):
Redirect to new registration (sign up) path if unauthenticated
The key is to override the route method, like so:
# app/lib/my_failure_app.rb
class MyFailureApp < Devise::FailureApp
def route(scope)
:new_user_registration_url
end
end
and then have Devise use your failure app:
# config/initializers/devise.rb
config.warden do |manager|
manager.failure_app = MyFailureApp
end
This approach is preferable to overriding authenticate_user! in your controller because it won't clobber a lot of "behind the scenes" stuff Devise does, such as storing the attempted URL so the user can be redirected after successful sign in.
With multiple user types
If you have Admin and User Devise resources, you'll probably want to keep the default "new session" functionality for admins. You can do so quite easily by checking what type of scope is being processed:
# app/lib/my_failure_app.rb
class MyFailureApp < Devise::FailureApp
def route(scope)
scope.to_sym == :user ? :new_user_registration_url : super
end
end
I need to add some simple methods and actions when a new user registers through Devise.
I want to apply a notify method which will send an email to me.
I want to use acts_as_network to pass a session value and connect the new register to the person who invited them.
How do I customize, I looked at the docs, but I'm not entirely clear what I need to do....thanks!
This is what I'm doing to override the Devise Registrations controller. I needed to catch an exception that can potentially be thrown when registering a new User but you can apply the same technique to customize your registration logic.
app/controllers/devise/custom/registrations_controller.rb
class Devise::Custom::RegistrationsController < Devise::RegistrationsController
def new
super # no customization, simply call the devise implementation
end
def create
begin
super # this calls Devise::RegistrationsController#create
rescue MyApp::Error => e
e.errors.each { |error| resource.errors.add :base, error }
clean_up_passwords(resource)
respond_with_navigational(resource) { render_with_scope :new }
end
end
def update
super # no customization, simply call the devise implementation
end
protected
def after_sign_up_path_for(resource)
new_user_session_path
end
def after_inactive_sign_up_path_for(resource)
new_user_session_path
end
end
Note that I created a new devise/custom directory structure under app/controllers where I placed my customized version of the RegistrationsController. As a result you'll need to move your devise registrations views from app/views/devise/registrations to app/views/devise/custom/registrations.
Also note that overriding the devise Registrations controller allows you to customize a few other things such as where to redirect a user after a successful registration. This is done by overriding the after_sign_up_path_for and/or after_inactive_sign_up_path_for methods.
routes.rb
devise_for :users,
:controllers => { :registrations => "devise/custom/registrations" }
This post may offer additional information you might be interested in.