[Django]-Authenticate by IP address in Django

15👍

There are two suitable approaches for that kind of authentication:

  • As Decorator: if some of views (but not many of them) requires this check, then it is better to write a decorator for that (something like @Jingo had written)
  • As Middleware: if that check needed to be done by all (or many) views, instead of using a decorator, writing a middleware is a better solution.

A sample middleware can be something like:

ALLOWED_IP_BLOCKS = [......]

class NeedToLoginMiddleware(object):
    def process_request(self, request):
        ip = request.META['REMOTE_ADDR']
        if not ip in ALLOWED_IP_BLOCKS: #ip check
            if not request.user.is_authenticated(): #if ip check failed, make authentication check
                return HttpResponseRedirect(...)
        return None
  • You can make ip check using a list, or a regex as @Jingo mentioned.
  • If you are using django authentication and REMOTE_ADDR is not in ALLOWED_IP_BLOCKS list, then you can use is_authenticated to check if related user had logged in or not. But for using is_authenticated in a custom middleware, your custom middleware must be placed after AuthenticationMiddleware, because request.user is set on that level.

    MIDDLEWARE_CLASSES = (
        ...
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'path.to.my.NeedToLoginMiddleware',
        ...
    )
    
    • If a few views do not require this authentication, then you can make a list of exceptional urls and get the request url from request.path and check if the request url requires ip check/authentication.

More info about custom middleware classes

👤Mp0int

8👍

You can also write a small decorator for this purpose:

def login_by_ip(view_func):
    def authorize(request, *args, **kwargs):
        user_ip = request.META['REMOTE_ADDR']
        for ip in allowedIps.allowedIps:
            authenticated_by_ip = re.compile(ip).match(user_ip)
            if authenticated_by_ip:
                return view_func(request, authenticated_by_ip, *args, **kwargs)
        return HttpResponseRedirect('/redirect/path/')
    return authorize

allowedIps is in my case a file (allowedIps.py) which stores the regexes for allowed IPs in a tuple like this:

allowedIps = ('^XXX\.XXX\..+\..+$','^XXX\.XXX\.XXX\..+$', '^XXX\.XXX\.XXX\.XXX$')

Hope this can help or give an idea.
Note: if you return authenticated_by_ip to the decorated view, your view will have to accept that parameter, you also can just ommit it, if you dont need it.
You can also define the regexes more precisely to only accept digits up to three.

👤Jingo

5👍

IMO, solving this with Django is fine if it’s a small non performance critical site.

It’s better to keep the unauthorized users fully at bay using your Apache or Nginx service. For example, in Nginx I have these lines in my site configuration:

include allowed_ips.conf;
deny all;
error_page 403 forbidden.html;

allowed_ips.conf is in /etc/nginx and looks (something) like this:

allow 110.222.333.222;  # J Bloggs (sys admin)
allow 777.222.0.0/16;   # Government owned
...

I believe this is better because the relatively slow Django processes never get touched by the blocked IPs. This is significant if you are blocking bots or other country address ranges for performance or security reasons.

4👍

You can try this decorator. I have tested its working fine:

allowedIps = ['129.0.0.1', '127.0.0.1']
def allow_by_ip(view_func):
    def authorize(request, *args, **kwargs):
        user_ip = request.META['REMOTE_ADDR']
        for ip in allowedIps:
            if ip==user_ip:
                return view_func(request, *args, **kwargs)
        return HttpResponse('Invalid Ip Access!')
    return authorize

3👍

There’s no need to write an authentication backend for the use case you have written. Writing an IP based dispatcher in the middleware layer will likely be sufficient

If your app’s url(s) is/are matched, process_request should check for an authenticated django user and match that user to a whitelist.

1👍

def login_by_id(request):
    ip = request.META['REMOTE_ADDR']
    try: UserProfile.objects.get(allow_ip=ip)
    except UserProfile.DoesNotExist: return HttpResponseRedirect('././')
    else:
        # auth here

You need allow_ip in you UserProfile model, which save on registration or changes on edit user page

Leave a comment