morgoth84
morgoth84

Reputation: 1140

Python/Django - HTTP authentication

I want to put basic HTTP authentication somewhere in my code if a certain condition is met. The condition is a request parameter. To simplify, let's say I want it in a Django view.

I know how to do it in PHP. This is the chunk of code which does exactly what I want:

if (isset($_REQUEST['key']) && $_REQUEST['key'] === 'value') {
    if (!isset($_SERVER['PHP_AUTH_USER'])) {
        header('WWW-Authenticate: Basic realm="My Realm"');
        header('HTTP/1.0 401 Unauthorized');
        echo 'Text to send if user hits Cancel button';
        exit;
    } else {
        echo "<p>Hello {$_SERVER['PHP_AUTH_USER']}.</p>";
        echo "<p>You entered {$_SERVER['PHP_AUTH_PW']} as your password.</p>";
    }
}

I can put this chunk of code anywhere in my PHP script and when execution hits those lines, authentication will be requested. If authentication fails, script execution will stop and a 401 Unauthorized header will be sent with the specified message. This is what I want in Python/Django. Here is a chunk of code, help me fill it in:

def some_view(request):
    if request.REQUEST.get('key') == 'value':
        # FILL ME IN

What I want:

What I DON'T want:

Upvotes: 1

Views: 1466

Answers (2)

knbk
knbk

Reputation: 53699

from django.http import HttpResponse

def some_view(request):
    if request.REQUEST.get('key') == 'value':
        if not request.user.is_authenticated():
            response = HttpResponse('Text to send if user hits Cancel button', status=401)
            response['WWW-Authenticate'] = 'Basic realm="My Realm'
            return response
        ...

This will return a 401 status code, but requires you to provide your own content/template. If it is acceptable for you to return a 403 status code, you can use the built-in PermissionDenied exception that returns a 403 page based on your '403.html' template:

from django.core.exceptions import PermissionDenied

def some_view(request):
    if request.REQUEST.get('key') == 'value':
        if not request.user.is_authenticated():
            raise PermissionDenied
        ...

EDIT:

To implement HTTP Basic Authentication, you have to set up your webserver (Apache, nginx, whatever you're using) to handle this. You should check your webserver's documentation on how to do this.

Django implements a RemoteUserBackend to allow users to login using basic authentication, but this uses the same user model as the normal ModelBackend, so these are not separate. Luckily, the middleware and the backend are quite simple. The webserver will deny any request with invalid credentials, so any time the 'REMOTE_USER' header (or whatever header value you use) is set, the user is properly authenticated. This header will be available in request.META, so to check if a user is authenticated using Basic Authentication, you can use this code:

from django.http import HttpResponse

def some_view(request):
    if request.REQUEST.get('key') == 'value':
        if request.META.get('REMOTE_USER', None):
            do_something()
        else:
            response = HttpResponse('Text to send if user hits Cancel button', status=401)
            response['WWW-Authenticate'] = 'Basic realm="My Realm'
            return response
        ...

Upvotes: 2

hellsgate
hellsgate

Reputation: 6005

I'd use a mixin with django's class based views for this

class AuthenticationMixin(object):
    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return HttpResponse('text, test, text', status=401)
        return super(AuthenticationMixin, self).dispatch(request, *args, **kwargs)

You would then use this in a view as follows

class MyView(AuthenticationMixin, DetailView):
.
.
.

If there is a view that you don't want to use authentication on all you need to do is not sub-class the authentication mixin in the class declaration

class MyUnauthenticatedView(DetailView):
.
.
.

Upvotes: 1

Related Questions