Code


Version 9 (modified by David Cramer <dcramer@…>, 7 years ago) (diff)

Fixed a bracket mismatch

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 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:

(r'^openid/$', 'myproject.openid.openid_form'),
(r'^openid/process/(?P<token>.*)/$', '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.

def initializeOpenID ():
    store = openid.store.filestore.FileOpenIDStore (OPENID_DATASTORE_PATH)
    global OID_CONSUMER
    OID_CONSUMER = openid.consumer.consumer.OpenIDConsumer (store)

initializeOpenID ()

Another approach is to use the main Django database:

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.)

@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 <q>%s</q>' + ': %s' % status 
                errors['url'] = fmt % (cgi.escape (openid_url),) 
            elif status == openid.consumer.consumer.PARSE_ERROR: 
                fmt = 'Could not find OpenID information in <q>%s</q>' 
                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:

@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:

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:

<form method="post" action=".">

<table>
<tr><th><label for="id_url">OpenID URL:</label></th><td>{{ form.url }}</td>
<td class="formError">{% if form.url.errors %}{{ form.url.errors|join:", " }}{% endif %}</td></tr>
</table>

<input type="submit" value="Authenticate"/>

</form>

A Implementation of this is available [here http://svn.zyons.python-hosting.com/trunk/zilbo/common/openid/]