Override Django authentication with middleware

pt2ph8 picture pt2ph8 · Nov 12, 2012 · Viewed 7.6k times · Source

I have a Django website and a MyBB forum, and I'd like to share authentication between them. My website used to be a message board; then I built a few other sections in Django, and both MyBB and Django run on the same domain. I've set up a system where upon registration (on the forum) every user gets two users: a Django user and a MyBB user. Users use the forum to log in, so I need Django to read MyBB's cookies and set the corresponding Django account as the logged user.

Can I do that with a middleware? This middleware would read MyBB's cookies (which contain the id of the MyBB user) and set request.user to the corresponding Django user. I'm new to Django, and I'm not sure if setting request.user (or calling authenticate) in a middleware is a good idea (or if there are better ways to do it).

Answer

maulik13 picture maulik13 · Nov 16, 2012

If the user_id stored in your MyBB cookie represents the same user in Django database then you can get the user object straight from that id using default Django backend. If those IDs don't match you need custom backend to get the Django user object. To get the user ID from MyBB cookie and update the user based on it, you need to have a custom authentication middleware.

Middleware

The main idea is to fetch the user object (based on your authentication logic) and assign it to request.user. Here is one example (not tested).

from django.contrib import auth

class MyBBMiddleware:
    def process_request(self, request):
        user_cookie_name = "session_key"
        if user_cookie_name not in request.COOKIES:
            # log user out if you want
            return 
        id = request.COOKIES.get(user_cookie_name)
        # this will find the right backend
        user = auth.authenticate(id) 
        request.user = user
        # if you want to persist this user with Django cookie do the following
        #auth.login(request, user)

Keep in mind that this is called for every request sent to your Django site. For performance you could cache the user and/or do a lazy object trick, EXAMPLE.

Backend

If you need to write your own logic for fetching user object and authenticating a user, you could do the following.

class MyBBCookieBackend(object):
    def authenticate(self, user_id):
        return self.get_user(user_id)
    def get_user(self, user_id):
        # if user_id is not the same in Django and MyBB tables, 
        #  you need some logic to relate them and fetch Django user
        try:
            #TODO your custom logic
            user = User.objects.get(id=user_id)
            return user
        except User.DoesNotExist:
            return None

You need to add your custom backend and middleware in the site settings file.