Django session authentication failed sometimes - django

I'm having very weird issue with session authentication.
I'm using session authentication over DRF APIs for some legacy django views. And login process implemented with DRF and react app.
The server code for sign-in looks like this:
class AuthViewSet(viewsets.ViewSet):
def create(self, request, *args, **kwargs):
is_session_auth = request.data.get('session_auth', False)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
token = serializer.save()
if is_session_auth is True:
login(request, token.user)
return Response(dict(token=token.key))
So, as far as I expected, the response should has set-cookie header which makes browser to set sessionid and csrftoken cookies. And it works well most of times.
But rarely, some users experiencing login failures. I'm faild to reproduce it, but this is what they describe:
When user submit sign in form, the request sent to the server and got response successfully.
Then the javascript app push url to login/complete, as expected.
But in login complete view, the request failed to authenticate. And the view redirect request to the original login view.
User retry login, but got same result.
I have no idea how this happened so rarely, to the so small amount of users. Am I miss something?
Thanks for help.
==============
Add some more information.
I dig into this problem, and find out that users' sessions are not decodable.
session.get_decoded() for session in Session.objects.all() returns Session data corrupted error. Is it relevant to login failure?

Related

How to implement admin login and logout using Django REST framework?

I have been given a task to authenticate admin login programmatically and logout as well.
I am able to to do login but on logged out when I check which user I am logging out it says AnonymousUser. How can I make sure I log out current user which is logged it.
I am using Django REST framework and testing it on Postman.
#api_view(["POST"])
def adminLogin(request):
if(request.method=="POST"):
username = request.data["username"]
password = request.data["password"]
authenticated_user = authenticate(request,username=username, password=password)
if authenticated_user != None:
if(authenticated_user.is_authenticated and authenticated_user.is_superuser):
login(request,authenticated_user)
return JsonResponse({"Message":"User is Authenticated. "})
else:
return JsonResponse({"message":"User is not authenticated. "})
else:
return JsonResponse({"Message":"Either User is not registered or password does not match"})
#api_view(["POST"])
def adminLogout(request):
print(request.user)
logout(request)
return JsonResponse({"message":"LoggedOut"})
Logging in/logging out with a REST API makes not much sense. The idea of logging in/logging out, at least how Django implements it, is by means of the session, so with a cookie that has the session id.
API clients like Postman usually do not work with cookies: each request is made more or less independent of the previous one. If you thus make the next request without a reference to the session, then the view will not link a user to that request. Clients like AJAX that runs on the browser of course can work with cookies, since these are embedded in the browser that manages cookies. You can work with cookies in postman as specified in this tutorial [learning postman], but this is usually not how an API is supposed to work.
This is why APIs usually work with a token, for example a JWT token. When authenticating, these are given a token that might be valid for a short amount of time, and subsequently it uses that token to make any other request that should be authorized.
As the Django REST framework documentation on TokenAuthentication [drf-doc] says, you can define views that create, and revoke tokens. The page also discusses session authentication that thus can be used for AJAX requests.
But likely you are thus using the wrong means to do proper authentication for your REST API, and you thus might want to work with a token like a JWT token instead.

Redirect From REST_API Response

I am using Angular and Django in my stack for a website, and after a user registers it emails them a link to activate their account. As of right now everything is working but the link takes the user to the Django rest framework page.
I've been returning responses in my app like this
data = {'success': True, 'message': 'An account has been activated.', 'response': {}}
return Response(data, status=status.HTTP_201_CREATED)
I am curious on how to redirect a user back to the login page which at the current moment would be a localhost page such as http://localhost:4200/#/authentication/login.
From research I have found methods like
return redirect('http://localhost:4200/#/authentication/login')
but I am wanting to keep my responses consistent. Is there a way to redirect a user while still using the rest api Response object?
After thinking about the comment posted by Muhammad Hassan, I realized I was thinking about this all wrong. Instead of sending a Django url I have change the email to send a URL to an Angular page I made and then I just sent an HTTP request from that page to the Django URL.

flask-login reusable cookies

I am using Flask-login with remember=False (the only cookie is the session cookie). When copy-pasting the session cookie after logging out, for some reason the session is still valid and the user is logged in. Even though the logged out session was deleted properly in the flask logout_user() function - meaning that the ["user_id"] was deleted from the session dictionary. It seems like the session is restored from the old cookie. can someone explain?
I do not really have a right answer for this yet, as I am investigating it myself, but there are a couple of points I would like to make here:
the logout_user() from Flask-login does not really seem to be invalidating the session. It just changes the 'session' cookie's value in the client (the browser). While in the backend this session is still alive.
An experiment to prove this would be: (a simple browser plugin like CookieManager can be used to perform this exercise)
login to the app
take a note of the 'session' cookie's value post successful login
now logout
now observer the 'session' cookie's value again. And you would
notice that it has now changed.
Replace this value with the 'session'cookie's value previously noted
in step 1 above.
Try visiting an internal authenticated page again.
Result : You would successfully be able to view an internal page without re-logging in, proving that the logout_user() never really invalidated the session but just changed the 'session' cookie in the client.
Howeverm, I am myself still taking a look into flask-login logout_user() definition and trying to make sense of it.
I had This issue too. After diagnosing what i found is the decorator #login_required *does not invalidate the User in server side after logout*, which is a security threat. This will be a cake walk for a Hacker to hack your application since they can easily extract all the request and header data from developer tool of your browser and can again send request to you server from outside of the application.For ex: If you have used any API in your application the it will be very easy for Hacker to get all the request data and resend a request using POSTMAN.
I solved this issue by creating a separate decorator "#authentication_required" and used in place of "#login_required". then it worked for me,though #login_required is supposed to do the same.
So basically while logging in i generated a random string(token) and sent to database and same string(token) is added to session of flask i.e session["token"]="akhfkjdbfnd334fndf" use any random string generator function.(session object is globally available if u r using flask . u can very well add any field to session). and while logout i again generate a string(token) and update the old token with newly generated token in database. So what #authentication_required will do is it will get the token from session object and the token which is present in database and try to compare the value. if both are same then only #authentication_required will let the client access api.and dont forget to do session.clear() after logout_user().
#---------------------------------------------------------------#
##authentication_required definition
def authentication_required(f):
#wraps(f)
def wrap(*args, **kwargs):
try:
user_id=session['user_id'] #assigning "user_id" from flask session object to a variable "user_id"
user=User_table.find_first(_id=user_id)#couhdb query syntax
#comparing api_token sent to session object at the login time with api_token in sent to database at login time. If both doesn't matches then user is redirected to login page.
if not session["token"]==user.token:
return redirect(url_for('login'))
else:
return f(*args, **kwargs)
except:
app.logger.info(Response('Request Did not came through header !', 401, {'WWW-Authenticate': 'Login failed'}))
return redirect(url_for('login_to system'))
return wrap
#---------------------------------------------------------------#
-------------------------------------------------------
login api code
#app.route('/login_to_system', methods=['GET', 'POST'])
def login_to_system():
form = LoginForm()
user = User_table.find_first(username=form.username.data)
login_user(user, remember=False)
try:
#Generating an random api_token at login time and will send to db
token_string=''.join(random.choices(string.ascii_uppercase + string.digits, k=14))
user.token=token_string #assigning token_string value to field api_token in database.
user.save() #saving the value in user table(using couch Db You can follow syntax as per you DB)
except Exception as error:
app.logger.error(str(error))
app.logger.info("before setting api_token in session")
session["token"]= token_string #adding a "token" to session object
#app.logger.info("Rendering login form")
return render_template('login.html', title='Sign In', form=form)
#-------------------------------------------------------#
#-----------------------------------#
#logout api code
#app.route('/logout')
def logout():
try:
user=User_table.find_first(_id=user_id)
#Generating a random token while logging out and will overwrite eariler token sent at login time send to database.
user.token=token_string=''.join(random.choices(string.ascii_uppercase + string.digits, k=17))
user.save()
except Exception as error:
app.logger.error(str(error))
logout_user()
session.clear()#clearing session
return redirect(url_for('Home page'))
#-----------------------------------#
Note: Seems like login_required is not working fine for me thats why i had to create another decorator but login_required also does the same thing but its strange that it not working for me.

Test REST service based on request.user

I build SPA on Django and I want to GET and POST JSON data based on request.user.
Something like this:
def get(self, *args, **kwargs):
return {
"data": [
i.get_json() for i in Customer.objects.filter(pk=self.request.user.pk)]
}
But I confuse, how it possible to put my user in request by REST service, like "Postman" or "Curl".
Postman has "Authorization" field, so I put login and password into it and my headers update with:
Authorization Basic YWdlbmN5X3NwYUBtYWlsLnJ1OjExMTEx
And I test it with curl:
curl -u myuser:11111 http://127.0.0.1:8000/api/myurl/
But user still - AnonymousUser
It could work with angular later, but I don't understand how I can test it now.
I found solution. I need to login with my user first and take sessionid from Cookie, then put sessionid in request.
Postman has nice extension named "Postman Interceptor", it put all session from browser into request authomaticly.

django tests response.request.user.is_authenticated() returning True after logout

I am trying to write some tests for the authentication part of my application and I encountered a problem with checking if the user is logged in or not. Here's the code:
client = Client()
# user signup form
response = client.post(signup_url, data={
'email': "lorem#ipsum.pl",
'password1': 'hunter2',
'password2': 'hunter2',
}, follow=True)
# checking if the user is logged in
with self.assertRaises(KeyError):
client.session['_auth_user_id']
self.assertEquals(len(mail.outbox), 1)
url = find_verification_url(mail.outbox[0].body)
response = client.get(url, follow=True)
self.assertEqual(200, response.status_code)
user = User.objects.get(email="lorem#ipsum.pl")
self.assertEqual(client.session['_auth_user_id'], user.pk)
# how to logout a user?
force_logout()
self.assertFalse(response.request.user.is_authenticated())
The user fills the form and clicks submit, then receives an email with a verification url. After he clicks the verification url in the email he's supposed to get directed to the site and authenticated. My questions is, what is a good way to find out if the user is authenticated or not? What is a preferred way to log out a user in this situation? I want to check that if the user is logged out and clicks the link the verification link second time it doesn't work. I tried some things like:
client.logout()
But unfortunately it seems to work once every two times even when I remove this line.
Thanks for any help!
Ok so the problem was that the authentication system was using a timestamp function to know if a url was expired or not. When run in a test the verification url was not expired when it should be. The login request after the logout was too fast and the system was thinking that the verification url was still valid and the user got authenticated. And that's why user.is_authenticated() was returning True all the time.