More best practices for authentication in Ruby on Rails

In a previous article we discussed some basic sensibilities about accessors in the controller and views, and storing authenticated sessions in your database.

First we’re going to take a conceptual look at credentials and then think about a clever way to deal with them in our application.

In an abstract way we share and exchange a private piece of information to identify a person. In most cases there is also a semi-public piece of information involved. We call this information the person’s credentials. Most of the times this takes the form of a password, and a username or e-mail address.

People can also authenticate themselves with just a shared secret, for example: a session token, SSL certificate, Kerberos ticket, API key, OAuth token, or PIN.

During login a person presents us with their credentials and we check them against our stored version.

Storing credentials

Don’t store plain text password. Try not to store any secrets as readable text when you don’t need to.

When your accounts always have exactly one set of credentials you can store them directly on the accounts table.

In a very tiny amount of cases you may store information about people separately from their accounts. Never make your database structure more complicated than it needs to be. You can always make things more complicated in the future.

As explained in the previous article I don’t like calling the table users. When the focus is on the credentials I like to use accounts. If it also needs to store a lot of personal information about the person, I like people.

Updating the password

Rails likes to use regular accessor methods when you’re writing forms, so let’s pretend our Account has a regular password field and then make it behave in such a way that it ‘just works’.

Remember to add translations for the errors.messages.too_short key, otherwise it will just say ‘Password too short’.

Now we need to select a nice encryption algorithm to secure the password with. I personally like SCrypt. Please be very careful to select an appropriate algorithm that fits your needs and stores the password in an acceptable way.

require 'scrypt'
# Automatically chooses a cost value for
# its algorithms based on the speed of
# the hardware used.
SCrypt::Engine.calibrate
class Account
def self.encrypt(password)
SCrypt::Password.create(password).to_s
end
end

We also need to be able to check the password against the value in the database.