This example requires that the Python OpenID library is already installed. You can find that at http://www.openidenabled.com/openid/libraries/python. In order to support [http://openid.net/ OpenID] authentication, you'll only need two views. They can be linked to whatever URLs you like, but I've picked "/openid/" and "/openid/process/", with the following entries in urls.py: {{{ #!python (r'^openid/$', 'myproject.openid.openid_form'), (r'^openid/process/(?P.*)/$', 'myproject.openid.process'), }}} The views need to make use of an OpenID consumer, which I initialize when the views.py file is loaded. The below also makes use of an additional setting that I placed into settings.py, a constant to indicate where the OpenID library should store its state information. {{{ #!python def initializeOpenID (): store = openid.store.filestore.FileOpenIDStore (OPENID_DATASTORE_PATH) global OID_CONSUMER OID_CONSUMER = openid.consumer.consumer.OpenIDConsumer (store) initializeOpenID () }}} You'll need one view to allow the user to specify an OpenID URL to authenticate. The following method uses another settings.py addition called SITE_TOP_URL. (If there's a better way to get or specify this information, I'd like to know.) {{{ #!python @login_required def openid_form (request): class OpenIDManipulator (formfields.Manipulator): def __init__ (self): self.fields = (formfields.TextField (field_name="url", length=30, maxlength=50, is_required=True),) manipulator = OpenIDManipulator () errors = dict () if request.method == 'POST': new_data = request.POST.copy () openid_url = request.POST['url'] if not openid_url: errors['url'] = ['You must enter an OpenID URL.'] if len (errors) == 0: status, info = OID_CONSUMER.beginAuth (openid_url) if status == openid.consumer.consumer.HTTP_FAILURE: fmt = 'Failed to retrieve %s' + ': %s' % status errors['url'] = fmt % (cgi.escape (openid_url),) elif status == openid.consumer.consumer.PARSE_ERROR: fmt = 'Could not find OpenID information in %s' errors['url'] = fmt % (cgi.escape (openid_url),) elif status == openid.consumer.consumer.SUCCESS: return_to = SITE_TOP_URL + '/openid/process/%s' % info.token redirect_url = OID_CONSUMER.constructRedirect (info, return_to, trust_root=SITE_TOP_URL) return HttpResponseRedirect (redirect_url) else: errors['url'] = ["Shouldn't happen"] else: new_data = {} errors = new_data = {} form = formfields.FormWrapper (manipulator, new_data, errors) return render_to_response ('openid/openid', {'form': form}) }}} And another to handle the result of the authentication: {{{ #!python @login_required def process (request, token=None): status, info = OID_CONSUMER.completeAuth (token, request.GET) if status == openid.consumer.consumer.FAILURE and info: message = 'Verification of "%s" failed.' % cgi.escape (info) elif status == openid.consumer.consumer.SUCCESS: if info: message = 'Successfully verified "%s".' % cgi.escape (info) else: message = 'Verification cancelled.' else: message = 'Verification failed.' request.user.add_message (message) return HttpResponseRedirect ('/') }}} Obviously, at this point you'll want to do something useful such as making a record of the correspondence between the current user and the provided OpenID URL, and then to allow an OpenID authentication to authenticate the corresponding native user. (I haven't added these in the current example, but they could both be accomplished by edits to the process() method above.) Finally, to allow the foregoing to actually work, you'll need some import statements at the top of your views.py: {{{ #!python import cgi import openid.store.filestore import openid.consumer.consumer from myproject.settings import OPENID_DATASTORE_PATH,SITE_TOP_URL }}} ... and have a form for the user to input their URL, with something like the following in it: {{{ #!xml
{{ form.url }} {% if form.url.errors %}{{ form.url.errors|join:", " %}{% endif %}
}}} A Implementation of this is available [here http://svn.zyons.python-hosting.com/trunk/zilbo/common/openid/]