Loading…

How to auto-authenticate a user in Django

Let’s say you are developing a Django app that will be used as part of a bigger Django project. As you’ve learned in the Django tutorial, you want to make that app reusable, so you develop it is a standalone Python package. But an app itself doesn’t run, it has to be part of a project. So in order to test and demonstrate your reusable app, you want to create a simple demo project.

If your app requires an authenticated user, your demo project needs a way to create and login a user. Out of the box, there are two options:

  • create a superuser with the management command createsuperuser and set up the Django admin to get a login page
  • create a user signup page and a login page

The first approach doesn’t need a lot of effort, but it is a bit cumbersome to use. The second approach requires quite a bit of code.

There is a more elegant solution to “fake” an authenticated user for every request:

You can create a middleware that automatically creates and logs in a user.

from django.conf import settings
from django.contrib.auth import login
from django.contrib.auth.models import User
from django.core.exceptions import ImproperlyConfigured

try:
    from django.utils.deprecation import MiddlewareMixin
except ImportError:
    MiddlewareMixin = object


class AlwaysAuthenticatedMiddleware(MiddlewareMixin):
    """
    Ensures that the request has an authenticated user.

    If the request doesn't have an authenticated user, it logs in a default
    user. If the default user doesn't exist, it is created.

    Will raise an ImproperlyConfiguredException when DEBUG=False, unless
    ALWAYS_AUTHENTICATED_DEBUG_ONLY is set to False.

    This middleware reads these settings:
    * ALWAYS_AUTHENTICATED_USERNAME (string):
      the name of the default user, defaults to `'user'`.
    * ALWAYS_AUTHENTICATED_USER_DEFAULTS (dict):
      additional default values to set when creating the user.
    * ALWAYS_AUTHENTICATED_DEBUG_ONLY:
      Set to `False` to allow running with DEBUG=False.
    """
    def __init__(self, *args, **kwargs):

        self.username = getattr(settings,
                                'ALWAYS_AUTHENTICATED_USERNAME',
                                'user')
        self.defaults = getattr(settings,
                                'ALWAYS_AUTHENTICATED_USER_DEFAULTS',
                                {})
        if (not settings.DEBUG and
                getattr(settings,'ALWAYS_AUTHENTICATED_DEBUG_ONLY', True)):
            raise ImproperlyConfigured(
                'DEBUG=%s, but AlwaysAuthenticatedMiddleware is configured to '
                'only run in debug mode.\n'
                'Remove AlwaysAuthenticatedMiddleware from '
                'MIDDLEWARE/MIDDLEWARE_CLASSES or set '
                'ALWAYS_AUTHENTICATED_DEBUG_ONLY to False.' % settings.DEBUG)
        super(AlwaysAuthenticatedMiddleware, self).__init__(*args, **kwargs)

    def process_request(self, request):
        if not request.user.is_authenticated():
            user, created = User.objects.get_or_create(username=self.username,
                                                       defaults=self.defaults)

            user.backend = settings.AUTHENTICATION_BACKENDS[0]
            login(request, user)

The middleware is quite simple: it checks if the user of the current request is authenticated, otherwise it logs in a default user that can be configured in the settings. If the user does not exists, he is created.

This middleware is available on Github and PyPI, so if you don’t want to copy and paste the above code, it is just a pip install django_always_authentication away.

Simply insert the middleware into settings.MIDDLEWARE (or settings.MIDDLEWARE_CLASSES if you are on Django <1.10) after django.contrib.auth.middleware.AuthenticationMiddleware and all you requests will have an authenticated user.

After some feedback on Reddit (thanks /u/raiderrobert), I want to add some words of warning: If you think about using this middleware for anything else than the aforementioned example project to demonstrate or test a re-usable Django app, please stop for a minute and think about the security implications this might have.

This post is a more elaborate version of my answer to this question on StackOverflow.

Leave a Reply