
I downloaded Rails Recipes by Chad Fowler today and was playing with the authentication recipe (#31). I like the book overall, and it’s got some good nitty-gritty solutions which are easy to implement.
Recipe #31 in particular is very simple which is important for the book overall - it makes it easy to get started and expand as needed (which is exactly the steps I went through). I thought I would post some code that expands the tool slightly and makes it a “mix-in-able” module. Structured this way, now any AR class that wants the functionality, can simply “include” the mix-in and create two database fields, and it’s authentication enabled.. I also added a method called “password_matches?” which provides a convenient way to compare a plaintext password against the salt/hash version in table (which is why I refactored some of Chad Fowler’s methods too). It seemed like this method was helpful when receiving form posts with authentication data stored in plaintext from the user..
[for ActiveRecord model file]
class User < ActiveRecord::Base
include Models::Passwords
end
[for a library file that the model file can "see"]:
# module for including mix-ins for model classes
module Models
# provides hashed password capability for Models
# Model which requires this module must have two fields: password_hash, password_salt
module Passwords
def password_salt
super || gen_new_salt
end
def password=(plaintext_pass)
# this line actually does something! if passsword_salt is not defined, this will create it for us
self.password_salt = self.password_salt
self.password_hash = gen_password_hash(plaintext_pass, self.password_salt)
end
def password_matches?(plaintext_password)
self.password_hash == gen_password_hash(plaintext_password, self.password_salt)
end
private
def gen_new_salt
[Array.new(6){rand(256).chr}.join].pack("m").chomp
end
def gen_password_hash(plaintext_pass, salt)
# code adapted from Rails Recipes v.1, p. 116
Digest::SHA256.hexdigest(plaintext_pass + salt)
end
end