Question

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

Was it helpful?

Solution 2

I think the right thing to will be a combination of middleware and a Django authentication backend.

Your middleware will call the backend's authenticate() with possibly the user id as a keyword argument. You authentication backend in turn will call the corresponding authenticate() method and return a user object.

class MiddlewareTracker:
 def process_request(self, request):
    id = request.COOKIES.get('logged_in_id') 
    authenticate(user_id = id)
    return None

class ForumAuthBackend(object):

    def authenticate(self, *args, **kwargs):
        id = kwargs.get('user_id')
        return User.objects.get(id = id)

    def get_user(self, user_id):
        return User.objects.get(id = user_id)

I will also recommend going through this https://docs.djangoproject.com/en/1.2/topics/auth/

OTHER TIPS

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.

Try to deal with request.session. request object comes as a first argument in view. More details about session here: https://docs.djangoproject.com/en/dev/topics/http/sessions/

You can definitely do that with middleware: More info about middleware here: https://docs.djangoproject.com/en/dev/topics/http/middleware/

I believe it's better to use process_view instead of process_request as process view has the following info:

  1. request
  2. The function to be called
  3. Passed arguments

process_view(request, view_func, view_args, view_kwargs):
    if not request.user.is_authenticated():
        return HttpResponseRedirect(settings.LOGIN_URL)
    return None

This makes it more flexible as we have function information(view_func) as well. So we can restrict this to some functions also by using:

view_func.__name_

which gives the function name.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top