Rails: Calling Devise authenticate_user! and handling invalid user/password exception - ruby-on-rails

I have a popup that will only allow to view/save some information if the user is authenticated.
I am using devise.
In the controller before_filter it checks if user is signed in and if not, show a sign in page.
This sign in page is ripped down version of the site's sign in page, so that it fits nicely to the popup.
On the authenticate action I call authenticate_user!. Everything works fine when the user enters valid credentials. But when the credential is invalid, devise automatically redirects to site's sign in page (which as I stated is different and not fit for a popup)
I tried appending a rescue to the call, but to no avail.
Anyone could suggest a better/right way to do this please? :)
def authenticate
authenticate_user! rescue redirect_to "/popup/sign_in"
if user_signed_in?
respond_to do |format|
format.html {
flash[:notice] = I18n.t("logged_in_succesfully")
redirect_back_or_default(accounts_path)
}
else
flash[:error] = I18n.t("devise.failure.invalid")
render "/popup/sign_in"
end
end

Related

Verifying user is authorized after using redirect_back

I have a method that creates a new user session and will redirect back to the previous screen after successful login. It has introduced a security issue where someone can type in a url navigate to a page they shouldn't have access to. The app will direct them to login, but after logging with valid credentials (just not the proper level of clearance) it will redirect them to the page they had manually typed into the url. How can I verify that redirect_back isn't sending a user to a page they shouldn't have access to?
Here is the session create method:
def create
#user = authenticate(params)
sign_in(#user) do |status|
if status.success?
redirect_back root_path
else
flash.now.alert = status.failure_message
render :new, status: :unauthorized
end
end
end
Is there a way to see what address it will be sending them back to because I could just do something like
if back_url.includes? "admin"
redirect_to root_path
end
You can’t rely on URL obscurity for security of your application. Your admin routes or controllers should be protected so they can only be accessed by signed in admins.
You can do this with a route constraint in your routes file (my preference) or a before action that is consistently applied across all of your admin controllers that returns a 403 if the current user is not an admin.

Rails - redirecting to previous page properly

In my application I am trying to enforce that users be signed in to preform certain actions. And when they try to preform these actions when not signed, they are redirected to the 'Sign-In' page, and once they have signed in, they are redirected back to the page they were trying to access. I have all of this working properly with the below code.
Controllers Code
if !(signed_in?)
session[:return_to] = newcompany_path
redirect_to signin_path
flash[:notice] = "You must be signed in to add companies."
else
#company = Company.new
#primary_field = ##fields
end
...
redirect_path = session[:return_to]
if user && user.authenticate(params[:session][:password])
sign_in user
session[:return_to] = nil
if (redirect_path == nil)
redirect_to user
else
redirect_to redirect_path
end
else
The issue I am running into is, if the user clicks away from the sign-in page without actually signing in, the session[return_to] variable is not cleared properly, and if they then go back to sign in later, they are redirected to the wrong page.
So, how would I set it up so if the user clicks away from the sign-in page, the session[:redirect_to] variable is reset?
You could pass the return_to path in query string parameter rather than the session and submit it alongside the sign in form so that it only applies for that form, rather than subsequent requests.
When redirecting to the sign in form, it'd look like:
redirect_to signin_path(return_to: newcompany_path)
Then, in your sign in form, make sure to include a hidden field with the return_to value, like:
hidden_field_tag :return_to, params[:return_to]
Then in your controller action for signing in, you'll check params[:return_to] rather than session[:return_to] to get the path you want.
Is this what you're looking for?
redirect_to request.referrer

Can I redirect back after sign in, if link is from email (in Rails)?

I have implemented redirect_to_back method in my ApplicationController for users that are not signed in (from this blogpost:
def redirect_to_back_or_default(default = root_url)
if request.env["HTTP_REFERER"].present? and request.env["HTTP_REFERER"] != request.env["REQUEST_URI"]
redirect_to :back
else
redirect_to dashboard_url
end
end
and I'm using in sessions_controller#create:
if #user && #user.authenticate(params[:password])
sign_in(#user)
redirect_to_back_or_default(dashboard_url)
else
...
The problem is, this only works if the request is coming from the application and not if it is a direct link, like in an email.
Is there a solution for this?
I would recommend altering how you accomplish this. If you follow the flow below I think it will produce the results you're looking for:
User arrives at URL (either by clicking link in email or typing it or whatever) but is not yet authenticated.
Store the URL that the user is at in the session
Redirect to the sign-in controller/action
After authenticating the user look in the session for where you stored the arrival URL
If present redirect to that URL (and clear stored URL out of the session) otherwise redirect to dashboard_url (it won't be present if someone navigates directly to the sign-in controller/action).

Submit form after devise login

I'm trying to let users submit a reservation request without being logged in. After they submit unauthed users are prompted to sign in or sign up. After signing up I'd like the form to be submitted and the (new registered) users to be taken to the checkout page.
Store location keeps the last page to return users after logging in. I need to figure out how to continue users on their intended path by submitting their forms and placing them on the checkout page after sign up/ sign in.
def store_location
#stores the last url. Used for post-login redirects to what the user was trying to do last.
if (!request.fullpath.match("/users/") && !request.xhr?) # don't store ajax calls
session[:previous_url] = request.fullpath
end
end
Ok, I think this is pretty dirty but I haven't been able to find another way to do this.
after_sign_in_path_for is a method Devise calls that allows you to send people to different pages after sign in.
I took all the create logic out of the controller and put it into a service object.
def after_sign_in_path_for(resource)
if session[:booking_params].present?
#booking = Booking::CreateBooking.new(user:current_user, params: session[:booking_params]).return_booking
checkout_booking_path(#booking.public_id)
else
session[:previous_url] || root_path
end
end
In the controller, the create method is split into two parts. If there is no current user I save their params into the session and they are sent to login. If there is the CreateBooking service object is called normally.
def create
if current_user.nil?
session[:booking_params] = params
redirect_to new_user_registration_path
else
#booking = Booking::CreateBooking.new(user:current_user, params:params).return_booking
respond_to do |format|
format.html { redirect_to checkout_booking_path(#booking.public_id) }
end
end
end
In the after_sign_in_path_for method I check for the session params and create the booking there.
Let me know if there is a better way to do this!

ruby rails - redirect to original request url

Here is what I have for redirecting to a default url(myapp_url). But I want to change redirect to go the request url was entered by the user after authentication.
How do I do that? I tried couple of options from searching here, like :back. But no go.
User enters an url, if not authenticated then gets redirected to the login page, then after login user shall be redirected to the original request url.
def create
user = User.Authenticate(params[:user_id], params[:password])
if user
session[:user_id] = user.id
redirect_to myapp_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
You can read the chapter about "Friendly forwarding" in the "Ruby on Rails Tutorial" by Michael Hartl to see how you can easily implement it.
Basically you have 3 helper methods:
store_location to store the user desired location in the session
redirect_back_or(url) redirect the user to the location stored in the session or, if not set, to the location contained in the url method param
and clear_return_to used "internally" by the redirect_back_or method to clear this piece of information once used
And then you use these methods:
A) when you see a guest user try to access a page that needs authentication use store_location before redirect him to the login page.
B) when a user is logged in, you use redirect_back_or(url) to redirect him to the right location (if present of course)
This is an overview of how this work, you get the idea but I suggest to read that chapter for the implementation (few) details.
You need to save path in session before redirection on authentication, and after successful auth redirect to this path.

Resources