Detecting user logout on browser close in Django - django

we have a web service for some numerical computing. It has a registered mode, in which a user has to register to have its results sent by mail.
We would like to keep track of how long the user stays logged. The login time is written in the database upon successful registration. Registration in not permanent, it's just for the purpose of single session and is used for acquiring the user email.
There are a few situations possible:
User logs out normally via the logout button.
Simplest solution. Write the time and logout in the database, and delete session.
User logs out by session expiry.
I'm planning on having a script which would check all the database entries which don't have a set logout time and if current time - login time > expiry time write logout time in a database as login time + expiry time.
User logs out by browser close.
The sessions have a get_expire_at_browser_close() set to True. But i don't know how can the server detect browser closure.
Ideas, critics, comments?

In django session middleware these lines control session expiration if we want that SESSION_EXPIRE_AT_BROWSER_CLOSE:
if settings.SESSION_EXPIRE_AT_BROWSER_CLOSE:
max_age = None
expires = None
Server doesn't have to do detect anything as cookie that has no max_age or expires set should be deleted on the client side, according to this page:
By setting either of these, the cookie will persist until its time runs out, otherwise—if you set neither—the cookie will last until you close your browser (a “session cookie”).
Edit:
One way of tracking how long user was online is by using javascript that will ping server every now and then. It will happen only as long as the user has page opened in browser and on every ping server should update last seen online value for the user.
When user closes browser session is over. Next time user logs in server can calculate duration of his last visit as last seen online - last login time.
Simpler solution without using any javascript: last seen online could be updated on every user request using simple custom middleware.

Related

How to force logout when Knox created token has expired?

I developed my Django based webapp with token authentication by following this tutorial of Brad Traversy (https://www.youtube.com/watch?v=0d7cIfiydAc) using Knox for authentication and React/Redux for the frontend. Login/logout works fine (here is Brad's code: https://github.com/bradtraversy/lead_manager_react_django/blob/master/leadmanager/frontend/src/actions/auth.js --> logout using a POST request), besides one issue: When the user stays away from the computer for a long time, the token expires in the meanwhile. So when the user returns he is still in the logged in zone of the website, but as soon as he opens a React component with data loading from the DB a 401 error is thrown in the console ("Failed to load resource: the server responded with a status of 401 (Unauthorized)"). Then the user has to go on "logout" and login again.
This is not optimal, I would prefer that after the user returns, the system realizes the token expiry and logs the user automatically out. I have thought of the following approaches, but I am not sure how to implement it or which one is best:
1) For every API request: if the answer is 401 --> logout (this might also log the user out in case the token has not expired, but if there is some other permission problem) - seems not optimal to me.
2) Instead one could also create a testing route e.g. api/auth/check with a Django view including the typical check
permission_classes = [permissions.IsAuthenticated]
and if 401 returned --> logout. So that would mean for every database request I have another rather unspecific database request before.
3) Check at every API request specifically if the token has expired --> how to do it? In the docs (https://james1345.github.io/django-rest-knox/) I couldn't find a method to check token validity. I see in the database table "knox_authtoken" an expiry date and a huge code in the column "digest", but this is obviously encrypted data and cannot be compared with the token value that one has in the browser under local storage.
I would be glad to receive recommendations on how to best implement this!
This can be done in multiple ways.
I dont see the reason kicking a user out automatically, but if you want to do that you can either:
Create an URL which will be only for checking if the authentication is valid every 5 secs or so
Use web sockets to send a realtime message once the token has expired.
Put the logic in the frontend, for example store how long the token is valid, and run a timeout, after the timeout is finished relocate him to login.
Jazzy's answer - option 3 - brought me on the right way (thank you!), but working with timers on the frontend side, was initially not successful, since starting a timer within a React component would only run as long as this component is visible. I have no component that is visible all the time of the user session. I changed the expiry duration of the token within Django settings from default value of 8 hours to 72 hours and implemented an idle check on the frontend with this package: https://www.npmjs.com/package/react-idle-timer . So as soon as my application is not used for 2 hours I call the logout action (api/auth/logout). With this approach I don't need to care about the expiry time of the token on Django side, since no user will be active throughout 72 hours. As soon as he logs in again, he will receive a new token.
New solution:
I decided to not bother users too often with logging in and found this nice strategy:
we choose to never expire Knox tokens
we set expiry date for Django session to 90 days from last login
if user does not log in for > 90 days, he will make at some point a request to the backend (e.g. data requests), there we include a check if the session data is available
if 'some_session_variable' in request.session:
# whatever logic you need
else:
return HttpResponse("logout")
Since session variable will not be available after the expiry the 'logout' string is returned. On the frontend we check every response for 'logout' string. If it is being returned we initiate the logout process. The idle timer is not used anymore (as it is not so reliable in my experience).

Session with Django

In a Home page, i have a form login. in the view.index of the app "Home", after authenticate, i create the ssesion. And after, i call the app "Places" if the authenticate is okey,
request.session['user'] = username
request.session.set_expiry(900)
return HttpResponseRedirect('/places/')
in the settings of the project i configure the SESSION_SAVE_EVERY_REQUEST = True.
How can i send the session to all others pages of the project, and log out the user when the session is expired ?
HTTP is a request response protocol.
This means that the server has no way to to communicate to the client without the client initiating the conversation. So the only way to do something like this is native Django, is to have the client periodically check to see if the session is still ok.
One way to achieve this is with a background ajax call (perhaps using setInterval in javascript) which checks the session, and if it's not any good anymore (either by expiration or the user has been disabled etc) then redirect them back to the login page.
Another approaches could involve sending the expiry time to the client so that it only checks the session when it would have expired (though this wouldn't pick up on users being disabled) or having a websocket server which pushes this information to the client.

Avoid updating session cookie expire time on request to django

I'm trying to ping Django from a javascript frontend to find out when a user's session will expire. I'm doing this so I can proactively notify a user when their session has expired.
Unfortunately, the session expire time is updated because I'm hitting the Django app. I've tried reading the session cookie from javascript, but it is not accessible (nor recommended to be accessible) from javascript.
How can I ping my Django app from javascript to get when the session will end?
What about passing the number of seconds until session will expire directly to your template/javascript? For example, you can get it using this method in your view function and pass it further.

Django session partially expired

I have a problem when using Django.I set :
session_expire_at_browser_close = true
So,When a user close the browser,his session expire.
Now I also record times of try to login in session.
request.session['try_times'] += 1
If a user try to many times,the website will ask for a verification code.
But because of the setting above,the session expire after restart the browser.
Is any method can meet the two requirements above at the same time?
That is to say,when the browser is closed,a user should be logout,and the 'try_times' should not be deleted.
Your can try to record the try_times on your server.

"Refresh" Django session variables to avoid session timeouts?

I have a multi-page Django signup process in which a user goes through the following steps:
Create an account (username, password)
Create a profile
Upload a photo
Review and approve/change profile and photo
Pass username and user ID to payment processor
Receive "Payment OK or Payment not OK" signal from payment processor
Log user in if "Payment OK" and display website's "home" page.
In step 1 above, the user's ID and a couple of other pieces of information are stored in a session. They're then examined when necessary during steps 2 through 4. The user ID and username will also be passed to the payment processor in step 5. I'm thinking of setting the session timeout period to either 30 minutes or an hour. Here's my question. Should I read and re-assign the session variables when the user GETs each of the above pages in order to help the user avoid having their session timed out? The Django documentation says Django only saves a session when the session has been modified (i.e. when any of the dictionary values have been assigned or deleted). I'm thinking that if I "refresh" the user's session as they move from page to page, it will be less likely that they'll be timed out and will thus experience a smoother signup process.
Any advice? Thanks.
There's SESSION_SAVE_EVERY_REQUEST setting that saves session and sends session cookie with every request, effectively turning session into sliding expiration session (btw, it's a widespread name for what you want to achieve)
Refer to session docs for details