Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Remote Authentication without redirecting

In my application I need to authenticate users via my REST API. So I have a form with user/pass fields, and after submitting it, I'd like to proceed directly to the 'next' page. So apparently I need to submit my form via AJAX, as I don't want to be redirected to the API page. But how then the RemoteUserMiddleware will know that my user should be authenticated if the request will be processed by javascript ?

like image 360
mike_hornbeck Avatar asked Nov 10 '13 23:11

mike_hornbeck


1 Answers

To my understanding of the system architecture you have currently looks something like the following:

       --------------             -------------------       -------------------
       | client web | ----------> |    REST API     | ----> | db / persistent |
       |   browser  | <---------- | pylons / nodejs | <---- |     storage     |
       --------------             -------------------       -------------------
            ^ |                         ^ | 
            | |                         | |
            | |                         | v 
            | |                   -----------------         -------------------
            | ------------------> |    django     | ------> | db / persistent |
            --------------------- |               | <------ |     storage     |
                                  -----------------         -------------------

Your question relates to how to log in and out users on the django app when the authentication is carried out in the REST API webapp.

I'm not sure that the RemoteUserMiddleware is what you are looking for, it is designed to allow authentication by an Apache webserver layer when django is run using wsgi on that same server. The name relates to the REMOTE_USER unix system variable which is an old school authentication method in apache.

It seems unwise to allow the client to be an intermediary in the authentication chain between django and your REST API, this would seem to be inherently insecure. Instead, django can call the REST API directly to authenticate users and then create a corresponding django.contrib.auth.models.User object to store locally this is carried out in a custom authentication backend, see here.

Something like:

from django.contrib.auth.models import User
import requests

class RestBackend(object):
    supports_inactive_user = False

    def authenticate(self, username=None, password=None):
        rest_response = requests.post('http://your.rest.interface/auth', 
            data={ 'username' : username, 'password' : password }).json()

        if rest_response['error'] == 'None':
            try:
                user = User.objects.get(username=username)
            except User.DoesNotExist:
                user = User(username=username, password=password)
                user.save()
            return user
        return user

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

This uses the requests library to call the REST API with a synchronous http request to log the user in and then creates a local instance of the User object, if one doesn't already exist. There are more complicated protocols for remote authentication, if required, http://oauth.net/2/ is an example.

This backend should be specified in the settings.py file

AUTHENTICATION_BACKENDS = ('my.classy.django.app.RestBackend')

Then your django app can use the authenticate and login functions in it's views, either using http or json, more info here.

Django will set the request.user to be an object of class AnonymousUser until the user is logged in, docs here. This allows you to differentiate between these users in your views without using redirects:

  from django.http import HttpResponse
  from django.utils import simplejson
  from myApp.models impor InfoObject

  def infoPage(request):
        # return info objects for logged in user, or all info objects otherwise
        if request.user.is_authenticated():
            infoObjects = InfoObject.objects.filter(user=request.user).orderby("-pubdate")
        else:
            infoObjects = InfoObject.objects.orderby("-pubdate")
        return HttpResponse(simplejson.dumps(infoObjects), content_type = "application/json") 

or if you wanted a 'user profile' box to appear on the page, ala stackoverflow:

 # helper function that can be called from all your views
 def getUserInfo(request):
      if request.user.is_authenticated():
          return UserInfo.objects.get(user=user)
      else:
          return []

 def randomPage(request):
       info = getUserInfo(request)
       .....other page logic....
       return HttpResponse('['+simplejson.dumps(..pageData..)+','+simplejson.dumps(info)+']', content_type = "application/json") 

If, instead, you are using templates and not ajax to render your page then this logic could be passed to the template, with areas appearing when a user logs in, without having to use redirects:

{% extends "base.html" %}

{% block userInfo %}
    <div id="userArea">
    {% if user.is_authenticated %}
        User: {{ user.username }}<br />
        geezer score: {{ userProfile.geezerScore }}<br />
        <input type="button" value="log out" />
    {% else %}
        Username: <input type="text" id="username" />
        password: <input type="password" id="password" />
        <input type="button" value="log in" />
    {% endif %}
    </div>
{% endblock %}

This relies on the user object being based into the template by the view, and would require the javascript to hook up the authenticate the backend.

It is also possible to use render_to_string() to render a context with a template, and return this to an ajax request instead of json. Thus allowing html to be rendered on the server and returned to the client without having to reload the page in the client.

In this manner it is possible to get django to render some templates and use some ajax responses to complement ajax requests to your REST interface.

Is this something like what you are looking for?

like image 187
Henry Florence Avatar answered Oct 30 '22 18:10

Henry Florence