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.
Related
In my application, I need to sign out specific users from time to time. And I need to do it from the interface or from a sidekiq worker. So I would like to create a sign_out method in my user model.
I saw in the documentation that devise provides a sign_out method but only in the controller. Is there a way to access to this method from a model or something similar.
Thanks
You need to read this answer https://stackoverflow.com/a/24388643/4269732 first.
If I was to implement this behavior then I would have added a column in User Model like expire_at_next_request?
Then just check this value in before_filter and logout the user if this is true.
class ApplicationController < ActionController::Base
before_filter :logout_if_requested
def logout_if_requested
if current_user && current_user.expire_at_next_request?
current_user.update_attributes(:expire_at_next_request=>false)
sign_out current_user
redirect_to :new_session_path
end
end
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.
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
Is it possible to redirect users to different pages (based on role) after signing in with Devise? It only seems to redirect to the root :to => ... page defined in routes.rb
By default Devise does route to root after it's actions. There is a nice article about overriding these actions on the Devise Wiki, https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-on-successful-sign-in
Or you can go even farther by setting stored_locations_for(resource) to nil, and then have different redirects for each action, ie: after_sign_up_path(resource), after_sign_in_path(resource) and so on.
You can simply add this method to your application controller:
def after_sign_in_path_for(resource)
user_path(current_user) # your path
end
Devise has a helper method after_sign_in_path_for which can be used to override the default Devise route to root after login/sign-in.
To implement a redirect to another path after login, simply add this method to your application controller.
#class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
users_path
end
Where users_path is the path that you want it to redirect to, User is the model name that corresponds to the model for Devise.
Note: If you used Admin as your model name for Devise, then it will be
#class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
admins_path
end
Also, if you generated controllers for Devise, then you can define this in the sessions controller, say, your Devise model is Admin, you can define this in the app/controllers/admins/sessions_controller.rb file to route to the dashboard_index_path:
# app/controllers/admins/sessions_controller.rb'
def after_sign_in_path_for(resource)
dashboard_index_path
end
And in the registrations controller - app/controllers/admins/registrations_controller.rb file:
# app/controllers/admins/registrations_controller.rb
def after_sign_up_path_for(resource)
dashboard_index_path
end
That's all.
I hope this helps
You can use the below code in the application controller or any controller where you need to do the operation:
def after_sign_in_path_for(resource)
users_path
end
https://github.com/heartcombo/devise/wiki/How-To:-Redirect-to-a-specific-page-on-successful-sign-in,-sign-up,-or-sign-out
I used example 1:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :authenticate_user!
protected
def after_sign_in_path_for(resource)
current_user.is_a?(Admin) ? admin_tests_path : (stored_location_for(resource) || root_path)
end
end
To expand on the answer provided by #Asnad Atta
Here is what I ended up using.
def after_sign_in_path_for(resource)
if current_user.role == 'admin'
start_path
else
news_path
end
end
I wanted everyone who logs in to first be pushed to a News page.
After the initial login the Home on the Navbar goes to the start_path (just what I call it in my app). The start_path is also in my root to:
This wonderful after_sign_in_path_for(resource) over rides the root to: path ONLY on the initial Login.
This is exactly what I wanted.
I hope this can help save someone some time.
Scott
Here's what I believe is the answer you are looking for from the devise wiki:
How To: Change the default sign_in and sign_out routes
I would like to customize my registrations controller for Devise in Rails. I understand that you must create a controller like this:
class AccountsController < Devise::SessionsController
def create
super
end
end
Well, that's all very good. But then let's say I want to fully control what happens in my #create action. How do I do that? How do I manually create a model and pass it all the params? Would Account.create(params[:account]) handle it smoothly? Is there some internal stuff going on I should know about or is my only option to call #super inside the action?
As long as you fulfil your required fields you can call Account.create in your example, I'm pretty sure the default Devise required fields are login, password and password_confirmation
We do this in a CRUD screen for creating devise users,
#admin = Admin.new(params[:admin])
if #admin.save
redirect_to admin_admins_path, :notice => 'New Administrator has been added'
else
render :action => "new"
end
and you don't want to extend the Devise session controller, a normal controller extending ApplicationController is fine or you can extend Devise::RegistrationsController and overwrite the methods you want to tweak in a registrations_controller.rb file
You can also have a look at the source on Github, if you want to be sure you're overriding things properly, and be sure you're not missing any processing...
https://github.com/plataformatec/devise/tree/master/app/controllers