Wednesday, August 30, 2006

Spyce will not waste your time: authentication

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:

  1. A minimum of customization to work out-of-the-box
  2. Gentle complexity slope when more sophisticated behavior is needed
  3. Play nice with others
  4. 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:

  1. Invoke the spy:login (or spy:login_required) tag in the pages you wish to restrict to authenticated users
  2. 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 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.)


Chris McDonough said...

Ouch. I resemble that "solve world hunger" remark. ;-)

zach said...

"Spyce handles the cookies, the login forms and handlers, all of the details you shouldn't have to worry about!"

Smells like Rails scaffolding. Nice for a demo, impractical in production settings. I suspect that by the time your authentication in Spyce looks and acts the way you need it to, you've written as much code as you would have in Rails (which is still probably not very much).

Karl Guertin said...

Note that the TG page you linked to is a year old. The current steps to the same thing are:

1. Create a quickstart project, answer 'yes' to the identity prompt
2. stick '@identity.require(identity.in_group("admin"))' in front of a method
3. tg-admin sql create
4. There is no default user/passwd or groups, so you still have to create those.

Jonathan Ellis said...

zach: the value of sensible defaults is not necessarily that you end up writing less code for production -- although much of the Spyce auth defaults are a lot more likely to be useful in production than scaffolding -- but that it lowers the learning curve. Time wasted flailing around trying to understand the steps needed to get started is still time wasted.

karl: thanks for the update; if there is a good doc page for this I would be glad to update my link. (That was the best I could google.)

johnS said...

How does spyce authentication compare with zope?

Tim Lesher said...

karl: thanks for the update; if there is a good doc page for this I would be glad to update my link.

By sheer coincidence, I just updated the TG identity docs yesterday:

Jonathan Ellis said...

Thanks, I updated the TG link.

John Reese said...

Just a phatic comment -- this is a good series of articles, keep it up.

Anonymous said...

The link posted regarding ASP.NET refers to ASP.NET 1.1; in ASP.NET 2.0, pre-built login controls are available which require no additional code. This is built on top of the provider model which allows different authentication backends