Creating a Twitter Authentication Backend in Django (pre OAuth)

I had the “joy” (yes joy) of getting to do this to work in Django and python and I’m not really kidding…I really did enjoy it.  There were a few curse words, but nothing got serious.  I decided to go for the inherited user model in this case since our models are really simple (I’ve heard the arguments against doing it this way and entertained other ways, this may not be the best but it is clean and simple).  So I had a model like:

class TwitterUser(User):
    twitterid = models.IntegerField()
    objects = UserManager()

Every twitter user has a unique id on the twitter system, so we will be using this to identify the user. You may need to add more info for your users (think timezones etc.) but that just depends on your app.

From here it is helpful to read Scott Barnham’s article on this subject. That way you will see why we are creating our model this way. From that model we can go on and write our Twitter auth backend. I won’t go into the details about how to create this type of auth backend because Scott’s article covers it much better. Primarily this is here to show how to incorporate the twitter authentication. In this case we do not store the password (this should be safer now with OAuth) and so we just use python-twitter to get an account/api instance for the user and then authenticate to twitter using their verify credentials api. You will have to poke around to get a feel for how to do the verify call, when I last checked, python-twitter had not wrapped this yet. I think pinax has an implementation in their “hotclub” code.

The final result should end up with something like this:

from django.conf import settings
from django.contrib.auth.backends import ModelBackend
from django.core.exceptions import ImproperlyConfigured
from django.db.models import get_model
from myapp.utils import *
from myapp.models import TwitterUser

class TwitterBackend(ModelBackend):
    def authenticate(self, username=None, password=None):
        acct = twitter_api(username, password)
        authd = twitter_verify_credentials(acct)
        if not authd:
            return None
        try:
            user = self.user_class.objects.get(username=username)
            return user
        except self.user_class.DoesNotExist:
            twitterid = twitter_userid(username)
            user = TwitterUser.objects.create(twitterid=twitterid,username=username, password='')
            user.save()
            return user
        except:
            return None
        return None

    def get_user(self, user_id):
        try:
            return self.user_class.objects.get(pk=user_id)
        except self.user_class.DoesNotExist:
            return None

    @property
    def user_class(self):
        if not hasattr(self, '_user_class'):
            self._user_class = get_model(*settings.CUSTOM_USER_MODEL.split('.', 2))
            if not self._user_class:
                raise ImproperlyConfigured('Could not get custom user model')
        return self._user_class

    def create_or_get_verified_user(username, password):
        try:
            user = self.user_class.objects.get(twitterid=twitter_userid(username=username))
            if user.check_password(password):
                return user
            else:
                user.set_password(password)
                user.save()
                return user
        except self.user_class.DoesNotExist:
            try:
                user = TwitterUser.objects.create(twitterid=twitter_userid(username), username=username, password=)
                user.save()
            except:
                return None

After you have that you will need to add the correct settings to your settings module, something like:

#Custom Authenticaiton Settings
AUTHENTICATION_BACKENDS = (
    'myapp.auth.twitter_auth.TwitterBackend',
    'django.contrib.auth.backends.ModelBackend',
)

Now you can do something in your view kind of like (your mileage may vary):

from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.views import login, logout
def myview(request):
    if request.user.is_authenticated():
        return HttpResponseRedirect('/loggedin/')
    else:
        if request.POST:
            authd = login(request)
            authd = request.user.is_authenticated()
            if authd:
                return HttpResponseRedirect('/loggedin/')
        logged_in = login(request)
        return logged_in

Hopefully that will help some people out, there is more to the views and what not, but you will have to hunt down your specific issues and design around them as needed.

Viewing 3 Comments

 
close Reblog this comment
blog comments powered by Disqus