2

I have a typical login form in my Django app template (which uses Bootstrap directly, not through some plugin):

  <form class="form-signin" method="post" action="/site_manager/login/" id="form-signin"> {% csrf_token %}
       <h2 class="form-signin-heading">Please sign in</h2>
      <div class="control-group">
          <label class="control-label" for="login">Login:</label>
          <div class="controls">
              <input size="50" name="username" id="username" required="true" type="text" class="form-control" placeholder="Login" intermediateChanges=false>
          </div>
      </div>
      <div class="control-group">
          <label class="control-label" for="password">Password:</label>
          <div class="controls">
              <input size="50" name="password" id="password" required="true" type="password" class="form-control" placeholder="Password" intermediateChanges=false>
          </div>
      </div>
      <button name="submit" id="submit" value="Log in" type="submit" class="btn btn-primary pull-right">Sign in</button>
  </form>

And here is the corresponding view which performs remote authentication through the requests module:

def login_view(request):
    if request.POST:
        username = request.POST.get('username')
        password = request.POST.get('password')
        headers = {'content-type': 'application/json', 'username':username, 'password':password}
        r = requests.get(remote_server_url, auth=(username, password), headers=headers)
        if r.status_code == 200:
            user = authenticate(username=username, password=password)
            if user == None:
                user = User.objects.create_user(username.encode('ascii', 'ignore'), "", password)
                user = authenticate(username=username, password=password)
            login(request, user)
            request.session.set_expiry(0)
            return HttpResponseRedirect('<index_page>')
        else:
            # redirects back to login page with some error message

Once this login succeeds, I can query the CSRF token with Javascript, as explained here, and my plan is to make Ajax calls to the remote server (over SSL) for other purposes (RESTful queries, for example). That server, as the above code suggests, uses basic authentication. So I want to set the CSRF token in the header of every Ajax call, but this does not follow the same-origin principle:

var csrftoken = $.cookie('csrftoken'); // using the jQuery Cookie plugin

$.ajaxSetup({
  headers: {
    'Authorization': "Basic XXXXX"
  }
  beforeSend: function(xhr, settings) {
    xhr.setRequestHeader("X-CSRFToken", csrftoken);
  }
});

$.ajax({
    type: "GET",
    dataType: "jsonp",
    url: remote_server_url+'/api/v1/someRESTfulResource/',
    contentType: 'application/json',
    success: function(data){
        // some operations with data
  }

});

The remote_server_url used in the Ajax call and the authentication are the same, and it does not share the same domain with the Django application. It is a security risk, as I gather. I also do not want to make plain text password available in the Javascript code for the same reason. How could I make Ajax calls to the remote server with the user credentials securely?

Subhamoy S.
  • 6,566
  • 10
  • 37
  • 53
  • 1
    Somewhat related: http://stackoverflow.com/questions/17931158/angularjs-django-rest-framework-cors-csrf-cookie-not-showing-up-in-client – Andrew Sledge Nov 04 '13 at 15:33
  • Can you show the ajax calls you are trying to attempt ? It should be straightforward. – karthikr Nov 04 '13 at 15:34
  • Updated the question :) @AndrewSledge: My case is a lot more basic and there is nothing except Django and jQuery involved. I cannot really salvage anything from the answer there that would help me here. – Subhamoy S. Nov 04 '13 at 15:55

1 Answers1

0

I am doing this on my Javascript initialization for a page I want to do Ajax POSTS with django:

$(document).ajaxSend(function(event, xhr, settings) {

  function sameOrigin(url) {                
    // url could be relative or scheme relative or absolute
    var host = document.location.host; // host + port
    var protocol = document.location.protocol;
    var sr_origin = '//' + host;
    var origin = protocol + sr_origin;              
    // Allow absolute or scheme relative URLs to same origin
    return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
        (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
        // or any other URL that isnt scheme relative or absolute i.e relative.
        !(/^(\/\/|http:|https:).*/.test(url));
  }
  function safeMethod(method) {
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
  }     
  if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
      xhr.setRequestHeader("X-CSRFToken", '{{ csrf_token }}');
  }
});

I don't remember where I've found it, however after using it I can do Ajax posts like this:

$.post("{% url 'foo' %}", "data="+myjson, function(data) {
  if(data['result']=="ok") {
    window.location.replace('{% url "bar" %}');
  } else {
    alert("Data Err!"); 
  }
}).error(function() { 
    alert("Ajax error!"); 
});
Serafeim
  • 14,962
  • 14
  • 91
  • 133
  • It looks to be following the same-origin principle also. If my Django app is running on my localhost, and I am making the Ajax calls to `https://www.suchandsuch.com/someservice`, will it work then? – Subhamoy S. Nov 04 '13 at 16:16
  • My code adds the CSRFToken to your post request so that you will be able to do a POST with django. However, you must understand that you cannot do POST requests to a different domain due to the same-origin-policy! If you want you can only do GET requests using JSONP. But then you won't need my code at all since CSRFToken is not needed for GET ! – Serafeim Nov 04 '13 at 16:20