If you work in the web development area, or even dabble in it as a hobbyist, sooner or later you're going to write code for a project that needs authentication. Probably sooner than later.
For a feature that gets used so frequently, it's remarkable to me that nobody has really done this right. Here are some basic principles for a good solution:
- A minimum of customization to work out-of-the-box
- Gentle complexity slope when more sophisticated behavior is needed
- Play nice with others
- Don't try to solve world hunger
The first two are, I hope, no-brainers. The second two bear more explanation.
Play nice with others: not everyone wants to authenticate against a Users table in a relational database. (Fairly common alternatives are LDAP or Unix logins.) If you bake in assumptions like this too deep, it causes problems. It might be worth the problems if it were impossible to provide both generality and ease of use, but such is not the case.
Don't try to solve world hunger. By this I mean, don't invent a Grand Unified Authorization/Permissions scheme with Groups and Subgroups and Roles and ACLs that supposedly can handle any needed authorization requirements, but requires a week of studying your system to make it work. There have been several attempts by very smart people to make this work and it hasn't, yet. So unless you are a grad student or in some other position that leaves you with too much time on your hands, don't go there. Instead, make it easy for the application developer to implement whatever he needs on top of your authentication code.
With that introduction, here's what you need to do to enable authentication for your Spyce app:
- Invoke the spy:login (or spy:login_required) tag in the pages you wish to restrict to authenticated users
- Write a function that, given a username / password combination, returns a pickle-able identifier, or None; make this function the login_defaultvalidator in your Spyce config file. Like this one, used in the pyweboff demo (which does, as it happens, store user information in a database):
def validator(login, password): user = db.users.selectone_by(name=login, password=password) if user: return user.id return None login_defaultvalidator = validator
That's it! Out of the box, Spyce handles the cookies, the login forms and handlers, all of the details you shouldn't have to worry about! The form rendering is done by active tags, which we talked about yesterday, so it's easy to customize when you get to that point. And if you want to restrict some pages to a subset of all users, you'd just write a validation function expressing those constraints, and pass that function to spy:login on those pages.
Here's a live example. (Login as spyce/spyce.)
If you'd like to compare this approach with other frameworks, here are some relevant links:
- Ruby on Rails: "Don't start here until you 'get' it. Most of the authentication guides are broken or poorly documented." Good luck! Or maybe you'd like to buy our book!
- Turbogears: (updated link) there is a lot to read here but Karl assures me that only a couple steps are actually necessary, besides the database setup.
- Django: you have to do a lot of reading to find out what the real minimum work to do is, but I count 5 steps in 3 files.
- I'll throw in ASP.NET for good measure: it's actually not too bad, but you're still on your own writing the login form and handler.
Tomorrow I'll talk about the Spyce db module, shown briefly above in the sample validation function. (And also mentioned in passing when I talked about Spyce handlers.)