Update: As of rails4 I still find the below mentioned session_countdown useful, but auto_hash has been replace with the rails native has_secure_password
Although there is an abundance of rails authentication/login plugins (Clearance, Devise/Warden, Authlogic, Simplest Auth, Restful Authentication, etc) I still find myself unsatisfied and therefore creating custom systems. If the abundance of authentication plugins hints at anything, it’s that many others feel the same way.
I have many reasons/rationalizations for wanting to do this. I often find these existing plugins to be too invasive; involving themselves in models, controllers, tasks, mailers, and I can’t imagine what else.
I also often find myself with unique authentication requirements, such as needing both a normal login form as well as a AJAX type interface, in the same application. Even situations where the password is optional.
I’m sure many of the existing authentication plugins can do many things when pushed, but then I find myself trying to bend opinionated software to my will, which honestly, I’ve had my fill of, being a rails developer.
Handmade authentication is not that difficult, and with the few simple plugins that I’ve created, its almost trivial. Below I show how to build an authentication system in rails 3.x with my new gems session_countdown and auto_hash.
I’m going to comment the code below with “SC” and “AH” to make it easy to spot where these two plugins are used.
You can view the docs and source for these gems at github:
http://github.com/kswope/session_countdown
http://github.com/kswope/auto_hash
Below are the steps to create a Ruby on Rails 3.x application that demonstrates how to use these plugins to create an authenticated website.


Lets get started
rails new handmade_authentication_demo
In Gemfile
gem "auto_hash"
gem "session_countdown"
Install the gems
bundle install
Generate the user model
shell> rails generate model user
In db/migrate/*_create_users.rb
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :email
t.string :password
t.timestamps
end
end
def self.down
drop_table :users
end
end
In app/models/user.rb ( this is one of auto_hash’s only two appearances, the other being password_hash_match?() )
class User < ActiveRecord::Base
auto_hash :password # AH
end
rake db:migrate
Create a new user from the console (don’t use seeds.rb, doesn’t run model hooks)
irb> u = User.new
irb> u.email = "adrian@example.com"
irb> u.password = "RamessesII"
irb> u.save
In config/routes.rb
root :to => "public#index"
match "login_screen",
:to => "public#login_screen",
:as => "login_screen"
match "login",
:to => "public#login",
:as => "login"
match "logout",
:to => "public#logout",
:as => "logout"
match "count",
:to => "public#count",
:as => "count"
Don’t forget to delete index.html, as I’m sure everybody does.
shell> rm public/index.html
We are just going use one controller for this simple demo:
shell> rails generate controller public
Add this code to app/controllers/public_controller.rb
skip_before_filter :authorize, :except => [:index]
def login
user = User.find_by_email params[:email]
if user && user.password == params[:password] # AH
session.countdown_start(1.minute) # SC
flash[:notice] = "Success logging in"
redirect_to session[:original_uri] || :root
else
flash[:notice] = "Email/Password wrong"
render :login_screen
end
end
def logout
session.countdown_abort # SC
flash[:notice] = "Logged out"
redirect_to :login_screen
end
def count
render :text => session.countdown_count.to_i # SC
end
Here is app/controllers/application_controller.rb. This is what puts your application behind an authentication wall
before_filter :authorize
def authorize
if session.countdown_running? # SC
session.countdown_restart # SC
else
session[:original_uri] = request.fullpath
if session.countdown_expired? # SC
flash[:notice] = "Login Expired"
else
flash[:notice] = "Please login"
end
redirect_to :login_screen
end
end
Login page app/views/public/login_screen.html.erb
<p><%= flash[:notice] %></p>
<%= form_tag(:login) do %>
<%= text_field_tag(:email, params[:email]) %>
<%= password_field_tag(:password) %>
<%= submit_tag("Log In") %>
<% end %>
Your index page (what’s behind the auth wall) app/views/public/index.html.erb. There’s an AJAX updater to show a countdown, demoing countdown_count().
<p><%= flash[:notice] %></p>
<span id="count"></span> seconds until login expires - refresh to "restart" countdown before it expires, or <%= link_to("logout", :logout) %>
<script type="text/javascript">
new Ajax.PeriodicalUpdater('count', '/count', {
frequency: 1, decay: 1
});
</script>
Adding a “Remember me” feature
If you want an “remember me” feature you need to do two things.
1. Set timer for far future when user checks “remember me”
session.countdown_start(1.year)
2. Tell rails to serve up a persistent cookie instead of session cookie:
In rails 3x, in config/initializers/session_store.rb
ActionController::Base.session = { :expire_after => 1.year }
In rails 2x, probably in app/controllers/application_controller.rb
ActionController::Base.session_options[:expire_after] = 1.year
Persistent vs session cookies explained
There are two types of browser cookies: ones with expiration dates and ones without. When a cookie doesnt have an expiration date it’s a session cookie and will be deleted when the browser quits. If the cookie has an expiration date it’s a persistent cookie (a.k.a. domain cookie) and will be valid until that date.
“Remember me” could work fine with only session cookies, provided the user never quits the browser, but users expect “remember me” to never expire their login and to persist across browser quits. It also makes sense to set a far future expiration date or the cookie will eventually expire before the login does.