11

I am trying to authenticate user on ajax post but doesn't work. Here what I have done

settings.py

LOGIN_URL = '/accounts/login/'
LOGIN_REDIRECT_URL = '/'

Template

<script>
  $('.btn-request').click(function(){
        var button = this;
        $.ajax({
                 type: "POST",
                 url: "{% url 'like' %}",
                 data: {'tutorial_id': $(this).attr('name'), 'csrfmiddlewaretoken': '{{csrf_token}}'},
                 dataType: "json",
                 success: function(json) {
                    toastr.success(json.message);

                  },
                  error: function(rs, e) {
                        alert(rs.responseText);
                  }
            });
      })
  </script>

urls.py

url(r'^like/$', 'apps.quotation.views.like', name='like'),

views.py - try 1

@login_required
def like(request):
        vars = {}
        if request.method == 'POST':
            response_dict = {}

            if not something:
                response_dict.update({'message': "Requested" })
            else:
                response_dict.update({'message': "You have already requested" })

        return HttpResponse(simplejson.dumps(response_dict),
                        mimetype='application/javascript')

views.py - try 2

def like(request):
    if not request.user.is_authenticated():
        return HttpResponseRedirect('/accounts/login')
    else:
        vars = {}
        if request.method == 'POST':
            response_dict = {}
            if not something:
                response_dict.update({'message': "Requested" })
            else:
                response_dict.update({'message': "You have already requested" })

        return HttpResponse(simplejson.dumps(response_dict),
                        mimetype='application/javascript')

Runserver log

[05/Mar/2014 05:19:16] "POST /like/ HTTP/1.1" 302 0
[05/Mar/2014 05:19:16] "GET /accounts/login/?next=/like/ HTTP/1.1" 200 5610

What I am missing?

MysticCodes
  • 3,092
  • 5
  • 25
  • 33
  • Are you logged in to manage the admin site ? The views.py try 2: "if not request.user.is_authenticated():return..." this is the same code I'm using, Why dont you show the user in the template ? {{request.user}} to enssure you're not logged in – AlvaroAV Mar 05 '14 at 11:30
  • I am not logged in to admin. – MysticCodes Mar 05 '14 at 11:35
  • {{request.user}} in template shows the AnonymousUser, I guess it means user is not authenticated? – MysticCodes Mar 05 '14 at 11:38
  • Yes the user is not authentificated, but it seems that you're managing unauthenticated users in some place. Try something like "if request.user == 'AnonymousUser': return..." – AlvaroAV Mar 05 '14 at 11:43

3 Answers3

14

When I want to check that when an Ajax call is made the user is logged in, here is what I use:

from functools import wraps
from django.core.exceptions import PermissionDenied

def ajax_login_required(view):
    @wraps(view)
    def wrapper(request, *args, **kwargs):
        if not request.user.is_authenticated():
            raise PermissionDenied
        return view(request, *args, **kwargs)
    return wrapper

raise PermissionDenied will cause a 403 status code to be returned to the client. Otherwise, if you use the @login_required decorator or perform manually a redirect to a form, what the Ajax call sees as a response is something that makes sense to a human being but not something that makes sense to an Ajax call.

Louis
  • 146,715
  • 28
  • 274
  • 320
  • 1
    I think if the issue is that the user isn't logged in and not that they don't have the proper permission, they should be given a 401 error and not a 403. – Tim Tisdall Dec 14 '15 at 15:12
  • The OP was not expression confusion about the status code to return but what mechanics to use to return something sensible when the request is AJAX. My answer can trivially be adapted to give a 401 instead of 403 if the developer so requires. It is certainly not true that 401 is *required* when a user is not logged in. (See [this answer](http://stackoverflow.com/a/14713094/1906307) for instance.) – Louis Dec 14 '15 at 15:28
  • @Louis Isn't the gist of that 401 vs. 403 link that: 401 = not authenticated (i.e. logged in) and; 403 = not authorised (i.e. logged in but doesn't have the correct permissions)? – CpILL May 25 '16 at 17:52
  • @CpILL That's not at all the gist of *the answer* I linked to. A lot of the other answers there make the distinction you mention between 401 and 403 but the answer I linked to specifically argues that this is not the correct distinction to make. 401 is for when your authentication mechanism is to use the `Authorization` header. None of the Django servers I have deployed use this mechanism. – Louis May 25 '16 at 18:07
  • @Louis at the end it says: "401 indicates that the resource cannot be provided, but the server is REQUESTING that the client log in" and "403 indicates that the resource can not be provided to the client given the current credentials, and different credentials might or might not produce different results"? Seems plain to me? – CpILL May 27 '16 at 09:38
  • 3
    You ended your first quote too early. Here's the relevant part: " the server is REQUESTING that the client log in **through HTTP Authentication and has sent reply headers to initiate the process**" I've bolded the important part. See, 401 is not a response you send any time a request cannot be completed because the user is not logged in. You send it **only** if you want the user to use "HTTP Authentication". And "HTTP Authentication" is not a generic term but a term that refers to a specific use of HTTP headers. By default, Django does not use this method, so 401 is inappropriate. – Louis May 27 '16 at 10:20
7

Instead of :

if not request.user.is_authenticated():
   return HttpResponseRedirect('/accounts/login')

return json response :

if request.user.is_authenticated():
    ## write your code...
    jsonr = json.dumps({ 'authenticated': True })
    return HttpResponse(jsonr, mimetype='application/json')
else:
    jsonr = json.dumps({ 'authenticated': False })
    return HttpResponse(jsonr, mimetype='application/json')

And At your ajax success response , if not authenticated then redirect to login using windows.location .

OR you can write decorator : Django authentication and Ajax - URLs that require login

Community
  • 1
  • 1
Priyank Patel
  • 3,495
  • 20
  • 20
-1

I am not sure whether it's an elegant solution but I made it to work as suggested by Priyank Patel

<script>
  $('.btn-request').click(function(){
        var button = this;
        $.ajax({
                 type: "POST",
                 url: "{% url 'like' %}",
                 data: {'tutorial_id': $(this).attr('name'), 'csrfmiddlewaretoken': '{{csrf_token}}'},
                 dataType: "json",
                 success: function(json) {
                    if(json.not_authenticated) {

                      window.location.replace("/accounts/login");
                    }
                    else {
                    toastr.success(json.message);
                    }  
                  },
                  error: function(rs, e) {
                    alert(rs.responseText);
                  }
            });
      })
  </script>

views.py

def like(request):
    response_dict = {}
    if request.user.is_authenticated():
        if request.method == 'POST':
            if not something:
                response_dict.update({'message': "Requested" })
            else:
                response_dict.update({'message': "You have already requested" })

        return HttpResponse(simplejson.dumps(response_dict),
                        mimetype='application/javascript')
    else:
        response_dict.update({'message': "Login please",'not_authenticated':True  })
        return HttpResponse(simplejson.dumps(response_dict),
                        mimetype='application/javascript')
Community
  • 1
  • 1
MysticCodes
  • 3,092
  • 5
  • 25
  • 33