LoginEngine is aging and was built on top of a quick-and-dirty
generator;
even James isn’t too happy with it anymore. acts_as_authenticated is
somewhat cleaner, but is a generator, and as with all generators,
there’s
no way to keep up with changes to the codebase. Neither is particularly
modular. And both of them provide some authorization as well as
authentication, and even a little user management, which further muddies
the waters.
So I’m proposing a new engine: Hark. It’s a pared-down, opinionated
engine
that answers only one question: “Who goes there?”
Hark has its roots in both LoginEngine and acts_as_authenticated. I’ve
borrowed liberally from each where their code was cleanest, and tried to
incorporate both sets of best practices.
The following features are not found in Hark, by design:
-
Authorization, such the login_required filter and its magic
redirection.
This is best performed by an authorization system such as
such as Bill K.'s excellent authorization plugin1. -
Support for extra fields. LoginEngine expected you to extend its User
model to add your own fields. Hark prefers that you create your own
User
model, and declare that it belongs_to :hark_user. You can thus create
any
fields you want in your own model. -
Support for delayed_delete. This is already handled by the excellent
acts_as_paranoid plugin2. -
Configuration options for validation. There are an infinite number of
ways you might want to validate passwords, logins, etc.; Hark doesn’t
try
to anticipate them. There’s a great DSL for that already called
“Rails”.
Instead, just create your own copy of the hark_user.rb model, and use
any
validations you like. The user code is all kept in a library, not in
hark_user.rb, so when you update to a newer version, you won’t lose your
customizations.
The following features ARE present:
- Passwords are hashed with a per-user salt value
- “Remember me” cookies
- E-mail verification upon registration (by release)
The following principles guide Hark’s development:
-
Extension over configuration. Rather than trying to build in
configuration options for every possible use case, make hark easy to
extend
without losing the benefits of shared code. That means: -
Be modular. All code, both controller and model, is in a properly
abstracted, verb-based library that takes care of things like cookie
creation, session management, attribute setting, etc. For instance, the
controller code for login looks like:def login
hs = Hark::Session.get(session, cookies)
unless hs.login(params[:hark])
flash.now[:warning] = ‘Login unsuccessful’
return
end
end
And this controller code itself is just the default login screen, which
will most likely be overwritten by your own controller.
-
Play nice with others. Hark should be able to take advantage of
plugins
like acts_as_paranoid, secure-action-plugin, etc. instead of trying to
roll
its own. -
Be paranoid. An awful lot of Rails code doesn’t check its state or
inputs, because the end result is usually nothing worse than an
“Application error”. In login code, we have to be a little more
careful.
To the extent that Rails allows, we should watch out for race
conditions,
session fixation attacks, etc. -
Be behavior-driven. All code in Hark is developed using rspec. I’ve
attached the current specdoc below. -
Be correct. That seems self-evident, but again, because Rails is so
easy
to work with, there’s a lot of “programming by coincidence”. To the
extent
that Rails allows, we should take advantage of locking, transactions,
and
update granularity so that things like multiple simultaneous logins are
not
an issue on larger systems. -
Be useful. I see too many posts saying that “AAA/LoginEngine/etc are
great starting points, but I always end up having to rewrite them.”
Yes, a
lot of authentication is system-specific - is your back end LDAP, or a
Rails table? - but the basic transaction sequences are usually the same.
Hark should be able to cope with that, while still providing a framework
for extension. You shouldn’t NEED to roll your own; there are too many
common mistakes that we already know how to avoid. -
Stay focused. Hark should not attempt authorization or user
management.
It’s purely about authentication and its dependencies, such as
registration
and verification.
So… that’s the goal. Right now, it’s in its infancy; I’ve just
completed
basic login and remember-me functionality, and I’m starting to work on
registration. The codebase is at http://svn.jay.fm/public/hark. It’s
not
even an engine at the moment, pending some resolution on the engines bug
with Dependencies in Edge Rails. I’d love to have someone to work with
on
this, especially someone with a systems background who can bring
additional
large-scale discipline to the project.
Interest? Questions?
Jay
specdoc:
The HarkController
- should allow Vlad the valid user to log in
- should have no current_user if Vlad provides the wrong password
The HarkController, given Louie the logged-in user
- should set current_user to be Louie
- should redirect Louie to the index page by default
- should redirect Louie back to his previous page if he has one
- should set Louie’s token if he asks to be remembered
- should prevent Louie from logging in twice
The HarkController, given Remmie the remembered user
- should log Remmie in automatically
A HarkSession, when Louie logs in
- should update Louie’s current last_login_at
- should have Louie as current_user
- should not have set any of Louie’s cookies
- should not have remembered Louie
- should prevent Louie from logging in again
A HarkSession, asked to remember Louie at login
- should set Louie’s hark_token cookie
- should remember Louie
A HarkUser in general
- should not be able to be created via ‘new’
Reggie the registering user
- should have an account created
- should have his e-mail marked as unverified
Reggie, when attempting to register
- should not be able to register without a password (FAILED - 1)
Vlad the valid user
- should be able to authenticate with his password
- should not be able to authenticate with the wrong password
- should be able to login with his password
- should not be able to login with the wrong password
- should be able to login with spaces around his username
- should be able to login with spaces around his password
Louie the logged-in user
- should get a remember_me token back when he asks
- should be able to log in with his token
- should not be able to log in with an expired token