Ruby on Rails
A_quick_and_dirty_homemade_authentication_solution

So you want to do authentication in Rails, but don’t want to find out which of the twentythousand Authentication systems might work for you (hint: try Acts_as_authenticated); here’s a quick rundown on how authentication basically works.

Method One: This may be vulnerable to SQL injection in Person.find(). See method two, below.

The ApplicationController contains our authentication system gatekeeper method.


class ApplicationController < ActionController::Base
  protected
    def authenticate
      unless session["person"]
        redirect_to :controller => "login" 
        return false
      end
    end
end

The controller that needs protection:


class WeblogController < ApplicationController
  before_filter :authenticate, :only => [ :index ]

  def index
    # show the secret stuff
  end
end

The controller that does login:


class LoginController < ApplicationController
  def index
    # show login screen
  end

  def login
    if session["person"] = Person.authenticate(params["name"], params["password"])
      redirect_to :controller => "weblog" 
    else
      redirect_to :action => "index" 
    end
  end
end

The model that does authentication:


class Person < ActiveRecord::Base
  def self.authenticate(name, password)
    find(:first, 
      :conditions => [ "name = '%s' AND password = '%s'", name, password ]
    )
  end
end

Ka-ching!


Method Two: Instead of using

find(:first, :conditions => [ "name = '%s' AND password = '%s'", name, password ] )
try using
find(:first, :conditions => { :name => n, :password => p }

This is not vulnerable to SQL injection.

Also, I want to keep server[‘REQUEST_URI’] the same through the login process. In other words, instead of redirect_to, I want to render a form requesting a username and password at whatever URL was requested. Then I don’t have to save/restore the URL in the session, and some browsers won’t even add the login page to the history.

So here’s an even shorter, and hopefully a more secure way of doing the same thing:

class ApplicationController < ActionController::Base

  protected
    def login_required
      session[:auth] ? yield : render(:template => 'login/index')
    end
end

Please explain how the check method is used

class LoginController < ApplicationController
  skip_filter :login_required

  def logout(msg = "")
    reset_session
    flash[:notice] = msg if msg.length
    redirect_to '/'
  end

  def check
    if not request.post?
      logout("Invalid request.")
    elsif session[:auth] = Person.find(:first, :conditions => { :name => params[:login][:n], :password => params[:login][:p] })
      redirect_to :back
    else
      logout("Your user name and password are invalid.")
    end
  end
end

The file app/helpers/login_helper.rb is also there (not mentioned above!) but only has two lines, and is not very interesting:

module LoginHelper
end

Controllers you want to protect should have around_filter :login_required or you can put around_filter :login_required in class ApplicationController and then un-protect controllers just like LoginController does, with

skip_filter :login_required

Only 26 lines of code. (Less code can lead to fewer bugs!) And you do still need views/login/index.rhtml with a form in it to send when the user is not logged in yet.
That would look like this:

Login Please


<= error_messages_for :login >
< form_for(:login) do |f| >

Username: <= f.text_field :n >

Password: <= f.text_field :p >
<= f.submit “Login”>
< end >