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 ()
}}}
Using global variables like above is '''bad''' practice. Try to avoid this if possible. -- NoahSlater
Another approach is to use the main Django database:
{{{
#!python
def _get_openid_consumer(request):
# Force Django to open the database:
django.db.connection.cursor()
store = SQLiteStore(django.db.connection.connection)
return Consumer(request.session, store)
}}}
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.)
You can use the ''sites'' module to get the current host name and construct a URL from there. -- NoahSlater
{{{
#!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

OpenID URL:

{{ form.url }}

{% if form.url.errors %}{{ form.url.errors|join:", " }}{% endif %}

}}}
A Implementation of this is available [http://svn.zyons.python-hosting.com/trunk/zilbo/common/openid/ here]
Some users reported problems getting this code to work. Another approach is to use the project [http://code.google.com/p/django-authopenid django-authopenid] which provides support for OpenID 2.x and has a working django example-project included. Read more [http://www.linuxuser.at/how-to-setup-a-django-project-with-openid-2-authentication here].