5

I have a private method authenticate_user! in my application controller which verifies the tokens in the header and returns a user record if found. Here is what it looks like.

def authenticate_user!
  # authenticate
  @current_login = Login.where(oauth2_token: bearer_token).first
  head 401 if @current_login.nil? # and return
  @current_user = @current_login.user
  head 401 if @current_user.nil? 
end

I use this method to authenticate the user in the controllers as follows.

class AddressesController < ApplicationController
   before_action :authenticate_user!

   def some_action
      data = @current_user.some_associated_records
      render json: {data: data}
   end
end

Ideally, when a login is not found or a corresponding user is not found, I should get a 401 response from the authenticate_user! method.

Instead, I always get a 500 internal server error. Somehow the head 401 if current_login.nil? does not halt the execution chain. Even render status: 401 doesn't do the job.

According to my understanding, rails returns if it finds a render or a head command in before_action filter. What is it that I am missing?

Edit:

The following solution works:

      private

      def authenticate_user!(*)
        @current_login = Login.where(oauth2_token: bearer_token).first!
        @current_user = @current_login.user
        rescue ActiveRecord::RecordNotFound
          head 401
      end

However I a still confused why the original approach does not work.

Rahul
  • 442
  • 6
  • 17
  • what's the backtrace on the 500? I notice "and return" is commented out? See also https://stackoverflow.com/questions/20623616/what-does-before-action-returning-false-do-in-rails-4 – rogerdpack Aug 18 '18 at 05:08
  • 1
    whats the difference between @current_login & current_login – praga2050 Aug 18 '18 at 05:09
  • 1
    500 is caused when `@current_user = @current_login.user` is executed. Basically when the tokens are wrong or not present, @current_login is nil and NoMethodError user for nil class is raised. There is no difference between current_login and @current_login. that is a typo. corrected it. – Rahul Aug 18 '18 at 05:19
  • according to my understanding, any render or head in before_action method should halt the execution of the rest of the action. So "and return" should not be required. Even if I use it, the result is the same. – Rahul Aug 18 '18 at 05:23

1 Answers1

0

As @Rahul mentioned (render/head in before_action does not stop executing the rest of the action) 500's error occurs on the step when you're trying to get a user from @current_login which is nil at this step.

head method just calls render nothing: true with provided status. And render doesn't breaks any execution chain.

According to above notes I'd suggest to rewrite it like this:

def authenticate_user!
  @current_login = Login.find_by(oauth2_token: bearer_token)
  @current_user = @current_login&.user
  head 401 if @current_user.nil? 
end
sfate
  • 155
  • 10