Deep linking problems on my Rails app - ruby-on-rails

EDIT I've provided more detail on my objectives below.
I am having trouble deep linking users to my site after they log in through the landing page using the Omninauth gem.
The most common scenario is this: a user receives an email with a deep link to the site - say www.mysite.com/suggestions/15. We've placed a Facebook login (through Omniauth) on the landing page, (def landing page on the Authorization controller - see below). When a user tries to access any other page without being logged in they are bounced back via the authenticate_user! helper method placed in the relevant controllers. authenticate_user is defined in the Application Controller -see below.
Once they get bounced back they click a "log in with facebook" button on the landing page and we create a session for the user through def authcreate. This works in the classic omniauth way, i.e. a callback from facebook which captures the session token. Now at this point I want to redirect the user to the page they were trying to get to (e.g. www.mysite.com/suggestions/15) but instead they only go through the default page (www.mysite.com/suggestions). This is not a problem when the user is already logged in, but never works when they are logged out.
You can ignore authorize! as that is a separate module (confusing I know) which is concerned with admin rights. Ditto on check_authorization.
I've created a module and placed it into lib/
module RedirectModule
def self.included(controller)
controller.send :helper_method, :redirect_to_target_or_default
end
def redirect_to_target_or_default(default, *args)
redirect_to(session[:return_to] || default, *args)
session[:return_to] = nil
end
def redirect_to_or_default(target, *args)
redirect_to(target || default, *args)
end
private
def store_target_location # Friendly redirect: store URL.
session[:return_to] = request.url
end
end
There is an include in the ApplicationController:
class ApplicationController < ActionController::Base
protect_from_forgery
check_authorization
include RedirectModule
def current_user
if session[:user_id]
#current_user ||= User.find(session[:user_id])
end
end
helper_method :current_user
def authenticate_user!
if !current_user
store_target_location
redirect_to "/"
end
end
I initiate the sessions on the following controller:
class AuthorizationController < ApplicationController
def landingpage
authorize! :auth, :landingpage
if current_user
redirect_to_target_or_default('/suggestions')
else
store_target_location
end
end
def authcreate
authorize! :auth, :authcreate
reset_session
authHash = env["omniauth.auth"]
existingUser = User.where(authHash.slice(:provider, :uid)).first
user = User.from_omniauth(authHash)
session[:user_id] = user.id
redirect_to_target_or_default('/suggestions')
end
def authdestroy
authorize! :auth, :authdestroy
session[:user_id] = nil
redirect_to "/"
end
end
What am I doing wrong?

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.

Restrict access in Rails app only for users logged in

I'm learning Rails and I'm trying to restrict access to pages if a user hasn't logged in and to only allow them to view the login and sign up pages.
Currently, my code creates a session when a user logs in and clears it when the user logs out. I've got a Sessions helper so that I can check whether a user is logged in but I'm unsure how to redirect the user throughout the app if he/she's not logged in.
UPDATE:
As I posted the question, I managed to get something to work with a before_filter. Should I use a before_action or before_filter?
Do I need to copy the same method in all my controllers where I want to restrict access?
CODE:
/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SessionsHelper
end
/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
redirect_to user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
log_out
redirect_to root_url
end
end
/helpers/sessions_helper.rb
module SessionsHelper
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
# Returns the current logged-in user (if any).
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
# Returns true if the user is logged in, false otherwise.
def logged_in?
!current_user.nil?
end
# Logs out the current user.
def log_out
session.delete(:user_id)
#current_user = nil
end
end
You can use a before_action. The rails guide has a nice section with an example on that:
class ApplicationController < ActionController::Base
before_action :require_login
private
def require_login
unless logged_in?
flash[:error] = "You must be logged in to access this section"
redirect_to new_login_url # halts request cycle
end
end
end

Rails too many redirects although it reaches desired page that doesn't redirect anywhere

I am trying to prevent a logged user from registering and hence I have the following code:
class UsersController < ApplicationController
include SessionHelper
before_action :new , :notLoggedIn
def notLoggedIn()
if logged_in?
redirect_to current_user
end
end
There is more code into the controller however it is irrelevant. (I think?). Here is the logged_in? function from the SessionHelper
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
# Returns true if the user is logged in, false otherwise.
def logged_in?
!current_user.nil?
end
Whenever I do log in (at the moment this is done automatically by registering. There is no page to actually login although I am not sure if this is relevant) the expected page users/7 is in the URL however the browser tells me that there was an infinite loop which in turn doesn't display the page.
The show method doesn't actually do anything apart from loading the user into a instance variable
def show
#user = User.find(params[:id])
end
Am I braking some rails principle that I don't know? Why does the browser display the relevant url but it doesn't render it and instead it tells me This webpage has a redirect loop
New is usually one of the RESTful actions, not a before action. Did you mean this?
before_action :notLoggedIn, :only => :new

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

Resources