Migrate (nearly) any encryption system to AuthLogic
Migrating users from a legacy password scheme to AuthLogic without resetting credentials.
- Published
- Reading time
- 3 min read
In winter 2008 I wrote the alpha version of PaperC, which uses an outdated version of acts_as_authentic. Spring 2009 all the alpha code was thrown away (it was untested, ugly and impotent) and the decision was made to rewrite PaperC from scratch.
As time goes by and plugins evolve, acts_as_authentic has been replaced by AuthLogic.
The problem I was facing: In Alpha State we’ve collected over one thousand users, who all wanted to be in Beta too. We couldn’t drop one of them. How to migrate the hashed passwords?
AuthLogic for the win!
AuthLogic was build to replace existing authentication plugins and stay as the dominant plugin for this use case. AuthLogic is so flexible, you can transition from (nearly) any encryption system. By the way AuthLogic can transition from some versions of acts_as_authentic automagically. But I’ll describe how to transition other combinations of salt/password encryptions.
A look inside
AuthLogic is very modular, so we can just write our own CryptoProvider class to handle the old encryption method. To accomplish this mission you’ll need to understand how AuthLogic passes the password/salt combination into a CryptoProvider class.
Note: At the time of writing I was using authlogic 1.4.0
AuthLogic takes the users password, tries if there is a match using the current encryption method. If it doesn’t match and you’ve configured it to transition from another encryption method, it will try to match the password using the old encryption method.
To do so it calls matches? on a CryptoProvider class with two arguments: the stored password and an array of encryption arguments (salt and password). The array looks like [raw_password, salt].
Given, your old encryption stores passwords this way:
def encrypt_password(password)
Digest::SHA512.hexdigest("--#{salt}--#{password}--")
endYou’ll need to write a simple class that mimiks the class method matches? of an AuthLogic CryptoProvider.
Mine looks like this:
class OldApplicationSha512
# AuthLogic Credentials provides the tokens in the following order:
# [raw_password, salt]
# The old application used '--#{salt}--#{password}--'
def self.matches?(crypted, *tokens)
Digest::SHA512.hexdigest("--#{tokens.last}--#{tokens.first}--") == crypted
end
endHere’s how to wire it up in your user model:
acts_as_authentic :login_field => :email,
:transition_from_crypto_provider => OldApplicationSha512