before_filter with devise - ruby-on-rails

I'm using Devise's built in before_filter :authenticate_user!. I want to call my own custom method in my application helper if a user fails the before filter (tries to do an action when logged out). How and where can I do this?

I would write a custom before filter that uses user_signed_in?. This will just return a boolean, and not do any of the redirect-type actions that authenticate_user! does.
So, you could write a before filter like this:
before_filter :custom_user_auth
...
def custom_user_auth
unless user_signed_in?
# Do custom stuff, ultimately restricting access to the
# ...protected resource if it needs to be
end
end
Do note that this before filter won't protect your resource from unauthorized users unless the inside area of that unless statement redirects or renders.

Instead of calling before_filter :authenticate_user! write your own function in your controller that calls authenticate_user!. Something like:
before_filter :logged_in
...
private
def logged_in
your_function
authenticate_user!
end

Related

How to share conditional redirect logic for multiple routes in Rails?

I'm using Devise for authentication in a Rails 6 app. Once a user logs in, I'd like to conditionally check that they have completed onboarding before allowing them to visit any and all authenticated routes. If they haven't, then they should be redirected back through the onboarding flow. Something like this:
unless current_user.has_completed_onboarding
redirect_to '/onboarding'
end
I have about a dozen routes where I want to implement this logic. What's the best way to add this check before each request to an authenticated route without duplicating it within each controller? Thanks!
what you can do in this type of case is write a method in the ApplicationController. Suppose check_user_onboarding
def check_user_onboarding
return redirect_to '/onboarding' unless current_user.has_completed_onboarding
end
Then in the controllers you can check by adding a before_action callback. The below one will check all the methods of that controller.
before_action :check_user_onboarding
You can also specify the methods that needs to be checked like -
before_action :check_user_onboarding, only: [:method1, :method2]
or there is also except
before_action :check_user_onboarding, except: [:method1, :method2]

How can I use Devise methods in my ConnectionAdapter callback?

I have a rails 5.1 app that's using Devise to handle authentication with my User model. This app has an Oracle database backend that requires setting a system context variable with the logged-in user prior to executing any queries, so I was hoping to do that in the :checkout callback for the ConnectionAdapter.
class ApplicationController < ActionController::Base
before_action :log_user
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.set_callback :checkout, :after do
# Would like to get the logged-in user's username here so I can apply
# it to the oracle sys_context.
# The below throws "undefined method 'user_signed_in?'"
username = current_user.username if user_signed_in?
end
def log_user
# When in this method, user_signed_in? and current_user work fine.
puts "User is #{current_user.username}" if user_signed_in?
end
end
The user_signed_in? method isn't found when run in the :checkout callback block, though it's generally available in the controller. Why?
Also, current_user within the block seems to evaluate to the current_user method defined within the ConnectionAdapter rather than the one defined by Devise. How can I get access to Devise's current_user?
How can I use these Devise-provided methods from within this callback?
You can't use the checkout callback, at the point that it's executed, it has no connection to the controller context. The fact that you've defined it here in your ApplicationController is irrelevant to the context it's actually executed in.
You will need to set the connection option in the before_action so you're running in the controller context. Something like:
before_action :set_user_context
def set_user_context
if current_user
ApplicationRecord.connection.execute "DBMS_SESSION.SET_CONTEXT('whatever', 'goes', 'here', '#{current_user.username}')"
end
end
...or something like that. Note that you might want to add a checkin callback to clear the value when the connection is finished with.
Btw, I answered a nearly identical question a few days ago: https://stackoverflow.com/a/54837596/152786 Different commands though, but might help.

devise_token_auth - is it possible to authenticate_user! without failing the filter chain?

I have an API that needs to return results based on whether there is a signed in user or not. I'm using devise_token_auth on the rails side, and ng_token_auth on the client side (Angular).
In my controller, I would like to do something like the following:
Try to authenticate_user, but don't fail the filter chain if authentication failed.
Use current_user when creating the response payload (current_user will either contain nil or the authenticated user).
Take this code for example:
class Api::MyController < Api::ApiController
before_filter :authenticate_user!
def index
if current_user
# Create the result json using current_user
else
# Create the result json without user data
end
render json: result
end
end
Only authenticated users will pass the authenticate_user! call and get to the index method. I'd like all users to get there, both authenticated and non-authenticated.
You could try the following (not tested because I currently don't have Rails installed).
skip_before_filter :authenticate_user!, only: [:index_auth_failed]
before_filter do
authenticate_user! rescue redirect_to index_auth_failed
end
def index
# current_user exists
end
def index_auth_failed
# current_user does not exist
end

how to prevent current_user is nil in devise when putting logic in view

I have the following code
- if current_user && !current_user.is_owner?(#product)
p do this
I added the current_user && logic because an error would occur if user is not logged in. but I am wondering if it is the correct way of doing it.
You will probably end up having to work around other problems in your views if you need the views to process parameters from the logged on user.
I would recommend you add a before_action that redirect to the login page if the current_user variable is not populated.
something like:
class YourController < ActionController::Base
before_action :redirectIfNoCurrentUser
** your functions/actions **
def redirectIfNoCurrentUser
redirect_to(new_user_session_path) unless current_user
end
end
before_action and skip_before_action can be configured in a per action basis with the :only option if required
You are right, this is already the correct way of doing this.
After it checks and sees that current_user returns false it knows the conditional will be false and does not execute the second part of the conditional.
It is a better practice of checking any value as current_user.present? instead of current_user because the previous one returns a boolean value.
You can try using authenticate_user! method of devise, If you do not want the user who is not logged in to access the page.
class YourController < ActionController::Base
before_action :authenticate_user!, only: [:your_actions]
def your_actions
end
end
Then in your views you can handle your conditions.
Your code is fine, but if you want to avoid littering your code with nil checks you may wish to consider using the null object pattern and make a NullUser with defined default behavior.
- if user_signed_in? && !current_user.is_owner?(#product)
p do this
Maybe you can try to use try method.
if current_user.try(:is_owner?, #product)
p do this
end

How to bypass authentication_user for only 1 method ? (using devise)

I am using devise and in my profile_controller.rb I have the usual 7 methods and an additional methods, now I am using before_filter as only authenticated user can access those methods but for just 1 method, I need it to bypass it. How to do it ?
before_filter :authenticate_user!
def index
...
end
...
def destroy
...
end
def edit_name
...
end
before_filter :authenticate_user!, except: :method_you_want_to_bypass
In this way you skip the call to authenticate_user! method when the current action is :method_you_want_to_bypass. This solution works in general, not only with Devise.

Resources