Michael
Michael

Reputation: 335

Django+React: What is the bet way to properly implement authentication and security on API requests from React to Django backend

I am working on a Django+React project. My React app is my only UI renderer (which means no template or any markup is being served by Django), while my Django app is only used for serving APIs for me to access my database resources.

On a particular React page, I have login and test buttons that send requests to a DRF API in my Django backend.

Login API View

class AuthLogin(generics.GenericAPIView):
    serializer_class = LoginSerializer
    authentication_classes = [SessionAuthentication]
    permission_classes = ()

    def get(self, request):
        user = request.user
        if not user and not user.is_authenticated or user.is_anonymous:
            return Response(
                {
                    "status": True,
                    "message": "Login page accessed"
                }, status=status.HTTP_200_OK
            )
        else:
            return Response(
                {
                    "status": False,
                    "message": "You are currently logged in"
                }, status=status.HTTP_400_BAD_REQUEST
            )

    def post(self, request, *args, **kwargs):
        res = []
        res_stat = status.HTTP_401_UNAUTHORIZED
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=False)
        validated_data = serializer.validated_data

        if validated_data:
            user = dj_auth(username=request.data.get('username'),
                           password=request.data.get('password'))

            if user and user.is_authenticated and not user.is_anonymous:
                auth_login(request, user)
                res_stat = status.HTTP_200_OK
                res = {
                    "status": True,
                    "message": "Login successful!",
                    "login": {
                        "date_time": timezone.now(),
                        "user": {
                            'id': user.id,
                            'first_name': user.first_name,
                            'last_name': user.last_name,
                            'email': user.email,
                        }
                    }
                }
            else:
                res_stat = status.HTTP_401_UNAUTHORIZED
                res = {
                    "status": False,
                    "message": "Invalid username and/or password"
                }
        else:
            res_stat = status.HTTP_401_UNAUTHORIZED
            res = {
                "status": False,
                "message": "Invalid username and/or password"
            }
        return Response(res, status=res_stat)

Test API View

class Test(APIView):
    authentication_classes = [SessionAuthentication]
    permission_classes = [IsAuthenticated]

    def post(self, request, *args, **kwargs):
        if request.user and request.user.is_authenticated and not request.user.is_anonymous:
            return Response({
                "test": "coolsss",
                "user": request.user.id
            }, status=status.HTTP_200_OK)
        else:
            return Response({
                "error": "Mind to login first?"
            }, status=status.HTTP_401_UNAUTHORIZED)

So far so good in terms of Django+React integration on the above APIs. However, I noticed a security issue and I'm not sure completely how to move on from this.

I did the test in an incognito browser window. But, I also tested it in Postman. What I noticed was I already closed my incognito browser before testing on to Postman using the same csrftoken and sessionid cookies. When I closed the browser, I assume the session is destroyed since it was in incognito.

Now when I run the same in Postman, the test endpoint still authenticating, recognizes the user, and returns the positive response. I'm not sure if that is supposed to happen, and what approach shall be used to resolve this.

One sample scenario I see where this would be a nightmare is say Kate logs in and leaves her workstation for a minute. Her workmate John sneaks in to her browser and steals the csrftoken and sessionid cookie values of her current session.

Kate came back and for some reason closed her browser. John, using Postman on his own computer, made some POST request to an API which Kate is authorized to access using the token and sessionid he stole.

With this problem in session/cookie-based authentication, I am considering implementing token-based authentication instead, even if the login view is more appropriate to be authenticated by a server session and csrftoken cookie.

However, if I use token-based, I would also be storing that token in local storage so React will have the knowledge that the user is still logged in and for it to use that token for subsequent requests. But then again, a geek can always go to dev tool and sneak into that token value in the local storage.

I am so stuck at how I should properly implement authentication and security in the backend, while keeping the frontend of the user's login state.

Upvotes: 0

Views: 778

Answers (1)

Navid Zarepak
Navid Zarepak

Reputation: 4208

Token-based (JWT for example) is the go-to option for integrating a server-side service to a client-side app. And as for the problem you are facing, the simplest option would be adding an expire option to the token which still might not be the solution to all scenarios.

The fact is that when you create a client-side app, the client has control and you can't do much about it. protecting the token is the same as protecting a password that users should be thinking about. The most you can do is to make it harder for someone else to be able to simply take the token and use it somewhere else.

You can make multiple tokens with different specifications that the client-side has to generate and if all of them match, then you can serve the request. but again, that just makes it harder for someone else to duplicate the request not impossible.

If it was a compiled app that was hard to decompile and with the combination of some methods like using some certificate file that is also securely stored in the client's system, you could be more sure about the safety of your request but JS is too simple to read and duplicate.

Upvotes: 1

Related Questions