DEV Community

Luis Solis
Luis Solis

Posted on

How to set token authentication via AJAX in Django to secure the API URL

After asking a question in stack overflow, by the way without finding an answer, but rather questions about why I did it this way, I saw that I was not the only one who had this approach.

So far no one has addressed this issue how to secure GET or POST requests in Django, AJAX, JS only that website can use it nobody else

Someone asked this question and this was my answer:

they always recommend "JWT, token authentication and session authentication", etc. But all those solutions require a user registered, and what happens if I want to protect a search autocomplete field or anything on the frontend that is public.

I have created a rest framework custom permission and I using django csrfmiddlewaretoken:

it behaves the same like Django post form, csrf token middleware send an input type hidden with

#{% csrf_token %}

(input hidden with the token) but and this case only needs

#{{ csrf_token }}

(the token), Django compares the request token with cookie token which are encrypted

permission.py
    from django.middleware.csrf import _compare_salted_tokens, rotate_token
    from rest_framework.permissions import BasePermission


    class HasCsrfTokenValid(BasePermission):
        def has_permission(self, request, view):
            token_valid = False
            try:
                csrf_token = request.headers.get("api-csrftoken")
                csrf_cookie = request.META.get("CSRF_COOKIE")

                """
                Check if both alphanumerics(strings) values are differents to prevent 
                a malicious user get the csrf cookie and send it from the ajax.
                """
                if csrf_token == csrf_cookie:
                    rotate_token(request)
                    return False

                token_valid =  _compare_salted_tokens(csrf_token, csrf_cookie)
            except ValueError: # if csrf_token & csrf_cookie are not a valid alphanumeric
                return False
            return token_valid

add permission class to api view

#views.py
class SnippetSearchAPI(generics.ListAPIView):
    model = Snippet
    serializer_class = SnippetSearcSerializer
    permission_classes = [HasCsrfTokenValid,]
    queryset = Snippet.objects.all()

//Ajax request:

$('#search-snippet').keyup(function () {
    $.ajax({
       url:"{% url 'snippet-search-space:api' %}",
       headers:{'api-csrftoken':'{{ csrf_token }}'},
       success:function(data){
         console.log(data)
       }
   });
 });

conclusion:

So far I have solved it, but I don't know the security implications for the future, I tried it and it seems safe. but there will always be someone out there who can hack this, at least I think so.

what do you think?

Oldest comments (6)

Collapse
 
mellen profile image
Matt Ellen

I'm sorry for being dense, but I don't understand what problem this is solving.

Do you want to use the csrf token in a GET request?

Collapse
 
agenteand profile image
Luis Solis

See the following situation:
I have a public endpoint(API) to use only on my website, e.g. a search form with an autocomplete field, how to protect this field to not expose to the spammer. you would tell me to use JWT or authentication token (Well)
What if an anonymous user wants to search for something on my website?
Do I ask him to register?
How to use the token without exposing it in the frontend in the ajax request?
I know that I can use quota limitation(in fact I will use it)
but if in addition to that I want only my website can use this endpoint nobody else
How do I do it without exposing my token?

Collapse
 
mellen profile image
Matt Ellen

Ah! I see. Thank you for clarifying.

Thread Thread
 
agenteand profile image
Luis Solis

you are welcome

Collapse
 
mellen profile image
Matt Ellen

When I had the requirement of creating an API and "eating our own dog food", I made the backend call the API, rather than have it happen at the front end.

That way the API key is locked to one user, but many people can use it.

Collapse
 
mohiuddinsumon profile image
Mohiuddin Sumon

in Django 3.2: _compare_salted_tokens is not found in csrf