I was innocently trying to add OpenID to an application, following the advice on Damian Cugley's article on using OpenID with Turbogears and on the TurboGears' documentation site's article on OpenID with Identity when I realised that it was way too much like hard work to implement. Edit this page, put this there, and so forth. Why couldn't I just import something and have it Just Work?
Well, it seems, that's not hard at all, actually. Thus, TGOpenIDLogin, which Just Works (well, python-openid Just Works, and I just use that) when you transfer login and so forth to it. It's a turboGears controller that you can hook up into any TurboGears application and have it take care of logging in of people using OpenID.
It can't be simpler:
from tgopenidlogin.controllers import OpenIDLoginController class Root(controllers.RootController): ... openid = OpenIDLoginController(User, VisitIdentity) login = openid.login logout = openid.logout
It remembers where you were trying to go, and comes with a simple OpenID form to put on any page which will remember what page you were on when you tried to log in - it's tgopenidlogin.widgets.OpenIDLoginForm.
You need to pass in the model for your User and VisitIdentity objects so that it can create users and update user details from their OpenID server, and so that it can log them on. Your User model needs to support usernames of 255 characters long. You can also pass in the web path to the OpenIDLoginController (it defaults to "/openid" relative to your web app base). You can pass in your own OpenID store, or it'll use a SQLite store (well, if you have pysqlite2 installed). You can also set your OpenID trust_root, or it'll default to the base of your web application.
Nothing invalidates logging in with plain username and password if you still want that. Your current login page can have a separate form (using widgets.OpenIDLoginForm, for simplicity) or a link to the TGOpenIDLoginController - just don't put in login = openid.login in your controller...
Still quite a bit to do:
- It doesn't save original_parameters like the standard TurboGears login does. This will require storing the parameters somewhere - probably the session.
- The User/VisitIdentity stuff might only work with SQLAlchemy with assign_mapper and Elixir and maybe SQLObject - non-assign_mapper SQLAlchemy will need a separate handler. This is probably easiest handled by making it easy to inherit from the controller.
- The post-authentication action, redirecting to the target page, might not be useful for places that want full registration. Again, probably best to stub it out with a default implementation, and let people inherit from the controller and override.
- Oh, I haven't really tested interoperability. I just used the example server from python-openid, and one that just failed, and few pages without OpenID server links.
And it's not yet possible to change the login screen template. That sounds like a pretty good idea...
I've been wondering about this for Durban, seeing as they're on a major drive to introduce user customization, which of course needs logins.
What concerns me is the amount of work required for, currently, very little benefit. I guess baking it in from the start is slightly different.
Will probably do a bit of research on OID on plone later this week though, maybe it's easy.
Nice :)
Just in case people would like a similar tool for CherryPy 3 only, I toyed around one a few months back:
Very interesting indeed. Thank for sharing.
With the MS attempted acquisition of Yahoo, and Yahoo taking on the OpenID standard, I see this being something that is needed in the next version of TG. Someone should talk to Kevin....