Reputation: 335
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
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