Tim Bodeit
Tim Bodeit

Reputation: 9693

Rails authentication and request variable not working as expected

I am developing a rails application, that allows optional HTTP basic authentication.

Authorization should be allowed, but not mandatory.

To do this, I am trying to use a before_action inside the application controller, that will try to find a user matching the given credentials and either write that user or nil into a global variable.

I tried this, but the block for authenticate_with_http_basic doesn't seem to be called at all (The console doesn't show the username and password, that I supplied, however logging outside of the block works):

class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception


  before_action :authenticate

    def authenticate
        authenticate_with_http_basic do |username, password|
            logger.info "Login:"+username+"  "+password
            @auth_user = User.authenticate(username, password)
        end
    end

end

And I tried this:

def authenticate
    if user = authenticate_with_http_basic { |username, password| User.authenticate(username, password) }
        @auth_user = user
    end
end

I also tried this, which throws an error undefined method 'split' for nil:NilClass. When looking at the documentation, I see that split is being called on part of the request. Am I doing something wrong with just assuming the request variable should be accessible from a before_action inside the application controller?

def authenticate
    username, password = ActionController::HttpAuthentication::Basic::user_name_and_password(request);
    logger.info "Login:"+username+"  "+password
    @auth_user = User.authenticate(username, password)
end

I just need a simple function that gives me username and password as string variables. What am I doing wrong? Is there another way to accomplish that seemingly simple functionality?

Update

The things I tried seem to work. My only mistake was to use a regular webbrowser to debug my API. Most web browsers don't send authorization to the server, before they get a www-authenticate header back, even if the user explicitly included it in the URL.

As long as it is just used as an API or accessed through other ways, this should not be a limitation. However, this kind of optional authorization, that does not present an authorization dialog doesn't work with regular browsers (at least not as a HTTP authorization). It is not a problem with Rails, just the way browsers are built.

Upvotes: 1

Views: 1592

Answers (1)

phoet
phoet

Reputation: 18835

you might just be using the wrong method. this is one of the examples from ApiDock:

class AdminController < ApplicationController
  before_filter :authenticate

  def authenticate
    authenticate_or_request_with_http_basic('Administration') do |username, password|
      username == 'admin' && password == 'password'
    end
  end
end

see this question for more details: In Ruby on Rails, what does authenticate_with_http_basic do?

UPDATE

i don't see any problems without requesting basic auth. it works as expected:

class HomeController < ApplicationController
  before_action :authenticate

  private

  def authenticate
    authenticate_with_http_basic do |username, password|
      logger.info "try basic-auth without requesting it: username=#{username} password=#{password}"
    end
  end
end

calling an action with credentials:

curl -I "http://uschi:[email protected]:5000/"

gives the following logs:

[hamburg.onruby.dev] [127.0.0.1] [044cb7ea-56a9-4f] Started HEAD "/" for 127.0.0.1 at 2013-10-21 17:40:54 +0200
[hamburg.onruby.dev] [127.0.0.1] [044cb7ea-56a9-4f] Processing by HomeController#index as */*
[hamburg.onruby.dev] [127.0.0.1] [044cb7ea-56a9-4f] try basic-auth without requesting it: username=uschi password=muschi

Upvotes: 1

Related Questions