How to log in a user right after signing up? - ruby-on-rails

I know I should put the code in the create action of the users controller, but I'm not sure what code I should put. I also assume it should call the create action in my sessions controller, but again I'm not sure how...
By the way I tried render :template => 'sessions/create' in the create action of the users controller, but I get this error when signing up:
Template is missing
Missing template sessions/create with {:locale=>[:en, :en], :formats=>[:html], :handlers=>[:rjs, :rhtml, :erb, :rxml, :builder]} in view paths "/rubyprograms/dreamstill/app/views", "/rubyprograms/dreamstill/vendor/plugins/facebox_render/app/views"
This is all in my application controller:
protected
# Returns the currently logged in user or nil if there isn't one
def current_user
return unless session[:user_id]
#current_user ||= User.find_by_id(session[:user_id])
end
# Make current_user available in templates as a helper
helper_method :current_user
# Filter method to enforce a login requirement
# Apply as a before_filter on any controller you want to protect
def authenticate
logged_in? ? true : access_denied
end
# Predicate method to test for a logged in user
def logged_in?
current_user.is_a? User
end
# Make logged_in? available in templates as a helper
helper_method :logged_in?
def access_denied
respond_to do |format|
format.html do
flash[:alert] = "You must log in to peform this action."
redirect_to root_path
end
format.js do
render_to_facebox(:partial => 'sessions/login_box')
end
end
false
end

Somewhere in your controllers you have something that looks like this:
user = User.new
# set attributes
user.save
render :template => 'sessions/create' # Probably based on your question
All you need to do is update the session to:
user = User.new
# set attributes
if(user.save)
session[:user_id] = user.id
# Send them somewhere useful
else
# Handle the error
end
They're signed in once session[:user_id] is set.

Technically?
In your controller, after you create your user, this code:
#current_user = user
should get you going (looks like you're using restful_authentication).
Now, whether it's a good idea to log in a user automatically without verifying their email address / whatever else is up for debate.

You seem that you just begin with Rails right ? I would highly recommend that you use a gem like Devise to handle your user registrations.
However, if you insist on doing it manually, you would just need to create a session variable that verifies whether a user is logged in or not. Then, you can add a helper like current_user, to get the user if user session shows he/she is logged in.
I see that you have a sessions controller there. Are you trying to use restful_authentication ? If so, once more i highly recommend switching to Devise :)
OLD CODE USING RESTFUL AUTHENTICATION - SESSIONS CONTROLLER
# This controller handles the login/logout function of the site.
class SessionsController < ApplicationController
# Be sure to include AuthenticationSystem in Application Controller instead
include AuthenticatedSystem
# render new.erb.html
def new
end
def create
logout_keeping_session!
user = User.authenticate(params[:login], params[:password])
if user
# Protects against session fixation attacks, causes request forgery
# protection if user resubmits an earlier form using back
# button. Uncomment if you understand the tradeoffs.
# reset_session
self.current_user = user
new_cookie_flag = (params[:remember_me] == "1")
handle_remember_cookie! new_cookie_flag
flash[:notice] = "Logged in successfully"
redirect_to :controller=>'Town'
else
note_failed_signin
#login = params[:login]
#remember_me = params[:remember_me]
render :action => 'new'
end
end
def destroy
logout_killing_session!
flash[:notice] = "You have been logged out."
redirect_back_or_default('/')
end
protected
# Track failed login attempts
def note_failed_signin
flash[:error] = "Couldn't log you in as '#{params[:login]}'"
logger.warn "Failed login for '#{params[:login]}' from #{request.remote_ip} at #{Time.now.utc}"
end
end

Related

Ruby on Rails - Can't set cookies in some actions

I'm trying to set in cookies target_path that non authorized user tried to reach and after authorization redirect him to the target. Everything works fine and good, but then I tried to set as target edit_test_path or create_test_path and other methods with POST/PATCH/PUT requests and seems that no cookies are being set. What can be the case?
application.rb - I'm setting cookies here. authenticate_user! calling in almost every controller before_actions
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
helper_method :current_user,
:logged_in?
private
def authenticate_user!
unless current_user
cookies[:target_path] = request.path_info
redirect_to login_path, alert: 'Verify Email or Password'
end
end
def current_user
#current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
end
def logged_in?
current_user.present?
end
end
sessions_controller.rb - I'm trying to redirect to the target from cookies here
class SessionsController < ApplicationController
def new; end
def create
user = User.find_by(email: params[:email])
if user&.authenticate(params[:password])
session[:user_id] = user.id
cookies[:target_path] ? (redirect_to cookies[:target_path]) : (redirect_to root_path) # With verb POST cookies don't work
else
flash.now[:alert] = 'Verify Email or Password'
render :new
end
end
def exit
session[:user_id] = nil
redirect_to login_path
end
end
I don't think, you can do this with POST/PUT/PATCH requests. When you are doing redirect_to, rails sends 302 Found reponse with location specified in parameter of redirect_to in your case cookies[:target_path] or root_path.
Browser then understends it should do redirect and sends GET request to URL specified in location header. You cannot nor should try to tell it to do POST/PUT/PATCH requests - these types of requests usually also require some kind of data (eg submitted form) that goes along with the request. You lost all of these data during redirect to login page anyway.
What I am trying to say - use these redirects only for GET requests. It will not work for POST/PUT/PATCH.

Friendly forwarding after login doesn't work after adding friendly_id to the user model

The core of my app is build from Michael Hartl's rails tutorial. Recently I changed the User DB so the name column is named username and I also added in friendly_id for prettier URLs. I updated everywhere I could find in accordance with these changes and everything works smoothly except for friendly forwarding after a user logs in. e.g. if a non logged in user visits a user edit page they are taken to the login page, after they login they should be taken to their edit page. But instead it just logs them in and stays on the login page.
I cant see anywhere that would need changing so it works again and from what I can see it should work
session controller
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
redirect_back_or user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
user controller
before_action :correct_user, only: [:edit, :update]
before_action :logged_in_user, only: [:edit, :update]
def edit
end
private
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
def correct_user
#user = User.friendly.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
application controller
def redirect_back_or(path)
redirect_to request.referer || path
end
private
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "You gotta login first!"
redirect_to login_url
end
end
session helper
# Redirects to stored location (or to the default).
def redirect_back_or(default)
redirect_to(session[:forwarding_url] || default)
session.delete(:forwarding_url)
end
# Stores the URL trying to be accessed.
def store_location
session[:forwarding_url] = request.url if request.get?
end
Anyone know where the problem might lie?
Update
after chatting with #vishal the redirect is done from the application controller redirect_back_or method
def redirect_back_or(path)
redirect_to request.referer || path
end
If I change it to
def redirect_back_or(path)
redirect_to root_path || path
end
I am taken to the root path so I know this line os most likely the culprit.
In-between it working and not working I added the mailboxer gem for private messages, changed the Users db name to username and added friendly_id to the username column. Maybe something there might stand out to you as a cause for it stop working.
In your application controller, change redirect_back_or(path) method to this.
def redirect_back_or(path)
redirect_to session[:forwarding_url] || path
session.delete(:forwarding_url)
end
You had redirect_to request.referer before which was responsible for redirecting you to the last url, that is, the login_url.
I have no idea why you're defining two different methods - 1 in ApplicationController & 1 in SessionsHelper - with the same name but different intended behaviors though.

Redirect logged in user using Omniauth in Rails

I've created a login controller so that people can log in using Facebook. When a user logs in he gets routed to frontpage/show and when they logout they get redirected to the root_url and it shows the login page.
The problem is that when a user logs in through Facebook, closes the site and then revisits the page he also gets directed to the root_url and not frontpage/show.
I've used Omniauth with Rails for this.
This is my session_controller.rb
class SessionsController < ApplicationController
def create
user = User.from_omniauth(env["omniauth.auth"])
session[:user_id] = user.id
redirect_to '/frontpage/show'
end
def destroy
session[:user_id] = nil
redirect_to root_url
end
end
and my frontpage_controller.rb
class FrontpageController < ApplicationController
def show
end
end
I've tried adding
def show
if authenticate_user?
redirect_to :controller=>'dashboard', :action => 'index'
else
redirect_to '/public/example_html_file.html'
end
end
To the frontpage_controller but it gives the error ndefined methodauthenticate_user` which makes sence. But I believe something like this is the answer.
Well, i'm assuming that you're using Devise. So, you have the method authenticate_user! and not authenticate_user?.
Now, to check if the user is logged, you can use the user_signed_in? instead of authenticate_user? and your code should works.
More information about those helper methods here https://github.com/plataformatec/devise#controller-filters-and-helpers

before_filter with method from another controller

I have a controller PostsController, which allows user to create posts before logging in. But to save it, user has to log in using Omniauth.
In PostsController, I have:
class PostsController < ApplicationController
before_filter :authenticate_user_by_service, :only => :create
def create
...
end
private
def authenticate_user_by_service
redirect_to user_omniauth_authorize_path(:facebook)
end
Now, I have another controller to handle callback from facebook, which is called ServicesController
class ServicesController < ApplicationController
def create
auth = request.env["omniauth.auth"]
... authentication logic here ...
sign_in(:user, service.user)
end
method_alias: :facebook, :create
Normally, for authentication, after sign in, I'll redirect to :back.
However, services#create here is used as a before_filter. In this case, what should I do to get it back to my Posts#create ?
Update: I got this warning saying the filter chain is interrupted right at the moment I refer to a different method
Started POST "/posts" for 127.0.0.1 at 2013-02-26 23:47:41 -0500
Processing by PostsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"H0as8=", "post"=>{"post"=>"bla bla"}, "commit"=>"Create Post"}
Redirected to http://localhost:3000/users/auth/facebook
Filter chain halted as :authenticate_user_by_service rendered or redirected
You're approaching this wrongly. You're handling login and the check wether someone is logged in in the same step.
Consider using a sessions_controller to handle all your signup/login/logout logic, for example:
class SessionsController < ApplicationController
def new # this will be /login
session[:return_to] = params[:returnto] unless params[:returnto].nil?
redirect_to "/auth/facebook"
end
def create # this will be the callback after the user is authenticated
auth_token = request.env["omniauth.auth"]["credentials"]["token"]
# you'll need to write this based on your app's requirement.
# Find a user or create one if he doesn't exist yet.
user = User.find_or_create_authenticated_user(auth_token)
if user.present?
session[:user_id] = user.id # this stores the current user's id in your session and lets Rails remember him for you.
redirect_to return_or(products_url) # see below for the usage of return_or
return
end
redirect_to root_url, alert: 'User not found or invalid'
end
def destroy # /logout
session[:user_id] = nil
redirect_to root_url
end
end
#routes.rb
match '/logout' => 'sessions#destroy', :as => :logout
match '/login' => 'sessions#new', :as => :login
match '/auth/facebook/callback' => 'sessions#create'
Then, in your ApplicationController you set up a couple of helper methods:
class ApplicationController < ActionController::Base
protected
# Use this in your views and controllers to get
# a handle of the user that is currently logged in.
# it will return nil if there is no user logged in.
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
# Use this to check wether a user is logged in.
# returns true if there is a user, false otherwise.
def logged_in?
!current_user.nil?
end
helper_method :logged_in?
# Block access to controllers or actions based on
# wether there's a user or not.
def require_login
unless logged_in?
# if we need the user to log in, we send him on his way
# but send him back to the current page afterwards.
session[:return_to] = request.fullpath
redirect_to root_url(subdomain: false), :alert => "Please login"
end
end
# When a user is not logged in, but you send him to log in,
# you can force him to return to the url he was in or if nothing
# was set go to a standard path.
# See this being set up in SessionsController#new and in require_login and then
# being used in SessionsController#create
def return_or(path)
session.delete(:return_to) || path
end
helper_method :return_or
end
These helper methods are available in all your controllers since they all inherit from ApplicationController. You could then tell your PostsController to send users who are not logged in to go and log in and after doing that they get returned to the PostsController.
Then to address your requirement of saving a post only after authentication: Either you do create the post, save it, but only update it to being public after the user is authenticated, or you store the contents of the post in the session and restore them after the user is authenticated:
class PostsController < ApplicationController
def new
#post = Post.new(session[:post_params] || {})
end
def create
if logged_in?
#post = Post.create(params[:post])
# ... etc
else
session[:post_params] = params[:post]
session[:return_to] = new_post_path
end
end
end
Note that this is a rather vulnerable approach. I would rather suggest to actually create the Post, mark it as not yet public and store only the post's id in the Session. After authentication you could then find that post_id, recreate the object from, set its status to public and associate it with the current_user.

rails specify method for redirect_to

During a post for a new model, I am checking for authentication via Authlogic. There is a before_filter on the create request. It is calling require_user. After the user session has been successfully created, the redirect_back_or_default(default) method is called. The problem is, The request needs to be posted to the stored uri. I have tried to store the method and input it into the redirect_to however it isn't working. Any ideas?
# called before successful authentication with before_filter
def require_user
unless current_user
store_location
flash[:notice] = "You must be logged in to access this page"
redirect_to new_user_session_url
return false
end
end
def store_location
session[:return_to] = request.request_uri
session[:return_to_method] = request.request_method
end
# called after successful authentication
def redirect_back_or_default(default)
redirect_to((session[:return_to] ? session[:return_to] : default), :method => session[:return_to_method])
session[:return_to] = nil
session[:return_to_method] = nil
end
You can't redirect to a post action, only get actions.
You could store the post object for later processing after authentication, but you really don't want to do that.
Why not simply ask for authentication on the #new method, rather than (or in addition to) the #create? That way the user is authenticated before they fill in the form.

Resources