DEV Community

Prevent Multiple Sessions for a User in your Django Application

Emmanuel Okiche on May 05, 2018

Welcome to my first tutorial on this platform. In this one i'm going to show you how to prevent a user account from having multiple sessions at the...
Collapse
 
shivamrohilla profile image
Shivam Rohilla

Thanks

Collapse
 
rhymes profile image
rhymes

Nice! BTW in a real world app instead of logging out the other user(s) you would probably tell them "Hey, stop streaming 50 videos :D".

Having more than one session could be accidental: I'm logged in on the phone and on the computer and I would hate to be automatically logged out on the other device.

Collapse
 
fleepgeek profile image
Emmanuel Okiche

That's very true.
Thanks for your comment on my first post here. I really appreciate.
My approach could be useful when the partner to a cheat in a relationship picks up the phone and the cheat is logged in on my tinder-like app. The cheater could quickly rush to his/her computer and log out. (LIFE SAVER SCENARIO)
The idea was just to show how to create a custom middleware. Like i stated in while concluding, this might not be the best approach.

Collapse
 
techxhelp profile image
techxhelp

I am getting this error when I uploaded the site to pythonanywhere (It was working fine locally)

Environment:

Request Method: GET
Request URL: careerabacusgallery.pythonanywhere...

Django Version: 4.0.1
Python Version: 3.9.5
Installed Applications:
['onlinetest.apps.OnlinetestConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'onlinetest.middleware.OneSessionPerUserMiddleware']

Traceback (most recent call last):
File "/home/careerabacusgallery/.virtualenvs/test/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "/home/careerabacusgallery/statelevel/onlinetest/middleware.py", line 19, in call
Session.objects.get(session_key=stored_session_key).delete()
File "/home/careerabacusgallery/.virtualenvs/test/lib/python3.9/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/careerabacusgallery/.virtualenvs/test/lib/python3.9/site-packages/django/db/models/query.py", line 439, in get
raise self.model.DoesNotExist(

Exception Type: DoesNotExist at /lgi
Exception Value: Session matching query does not exist.

Collapse
 
dhruv354 profile image
dhruv354

my signals.py

from django.contrib.auth import user_logged_in, user_logged_out
from django.dispatch import receiver
from .models import User, LoggedInUser
from django.contrib.sessions.models import Session
from accounts.models import User, LoggedInUser, UserSession

@receiver(user_logged_in)
def when_user_logs_in(sender, request, **kwargs):
print('user_logs_signal is called')
LoggedInUser.objects.get_or_create(user=kwargs.get('user'))

@receiver(user_logged_out)
def when_user_logs_out(sender, request, **kwargs):
print('user logs out signal iscalled')
LoggedInUser.objects.get_or_create(user=kwargs.get('user')).delete()

my models.py

class LoggedInUser(models.Model):
user = models.OneToOneField(User, related_name='logged_in_user', on_delete =models.CASCADE, null=True, blank=True)
session_key = models.CharField(max_length=32, null=True, blank=True)

my apps.py

from django.apps import AppConfig

class AccountsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "accounts"

def ready(self):
    import accounts.signals
Enter fullscreen mode Exit fullscreen mode

i have added the app in settings.py but my signals are not working please help

Collapse
 
olleugra profile image
olleugra

this one works for me:
class OneSessionPerUserMiddleware:
# Called only once when the web server starts
def init(self, get_response):
self.get_response = get_response

def call(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
if request.user.is_authenticated:
session_key = request.session.session_key

try:
logged_in_user = request.user.logged_in_user
stored_session_key = logged_in_user.session_key
# stored_session_key exists so delete it if it's different
if stored_session_key and stored_session_key != request.session.session_key:
Session.objects.get(session_key=stored_session_key).delete()
request.user.logged_in_user.session_key = request.session.session_key
request.user.logged_in_user.save()
except LoggedInUser.DoesNotExist:
LoggedInUser.objects.create(user=request.user, session_key=session_key)
stored_session_key = request.user.logged_in_user.session_key

# if there is a stored_session_key in our database and it is
# different from the current session, delete the stored_session_key
# session_key with from the Session table
if stored_session_key and stored_session_key != request.session.session_key:
Session.objects.get(session_key=stored_session_key).delete()

request.user.logged_in_user.session_key = request.session.session_key
request.user.logged_in_user.save()

response = self.get_response(request)

# This is where you add any extra code to be executed for each request/response after
# the view is called.
# For this tutorial, we're not adding any code so we just return the response

return response

Collapse
 
tlbrea profile image
Anthony Brea • Edited

class OneSessionPerUserMiddleware:

Called only once when the web server starts

def init(self, get_response):
self.get_response = get_response

How do you implement this code? Could you spell out where this code should reside? Which directories, files etc ...?

Collapse
 
pyb1l profile image
Papavassiliou Vassilis

Thanks for the tutorial,
Is there any way to enforce this behavior without user interaction to website?
For instance in livestream scenarios if a user logins with credentials from device A and shares credentials to device B, if user never interacts with device A both could watch the livestream

Collapse
 
fleepgeek profile image
Emmanuel Okiche

Good question.
The approach used here would log out the user whenever he/she makes any request.
For your streaming example, something like Django channels would need to be used.

So you can take the logic used in my middleware and integrate it with Django channels.
So immediately the user logs in from another device, you send the logout event to the first device and maybe redirect to the login page.

Django channels supports the standard Django authentication out of the box.

Collapse
 
lecarrou profile image
LECARROU

Hi Emmanuel,

thanks a lot for this tutorial. I have implemented it on one of my project and it works except in twp situations:

  1. if user open multiple tabs in the same browser
  2. if user change his password: error Session objects doesn't exist

The second issue is due to Django behavior when updating password that update session_key. So when we want to delete, previous session_key no more exist

I just add a condition to check for session_key exsit before deleting and it seems to works but need to investigate more in detail.

if stored_session_key and stored_session_key != request.session.session_key:
if Session.objects.filter(session_key=stored_session_key).exists():
Session.objects.get(session_key=stored_session_key).delete()

Do you had the same issues? How do you solve it?

Collapse
 
dhruv354 profile image
dhruv354

request.user.is_authenticated always coming as false for me so i am not going inside that middleware what should i do

Collapse
 
fleepgeek profile image
Emmanuel Okiche • Edited

It means you're not logged in.
To confirm if you're loggedn in print out request.user.
If it returns AnonymousUser then it means you're not logged in correctly.

Collapse
 
dhruv354 profile image
dhruv354

Yes that was my mistake i solved , please help me in my last error. Error is that my signals are not firing i had added the signals as mentioned, also i imported signals in apps.py and added my app in settings.py but they are not working please help me in this issue

Collapse
 
diegolipa profile image
Diego Frank Lipa Choque

Gracias Excelente. me ayudo bastante ✔😃👌

Collapse
 
fleepgeek profile image
Emmanuel Okiche

Thank you. I'm glad it was helpful to you. Gracias amigo

Collapse
 
chargetank profile image
Charge Tank

Is there a specific reason why you do this check with middleware (every request)? Why don't you just check once when the user logs in?

Collapse
 
fleepgeek profile image
Emmanuel Okiche

Did that for demonstration purpose just to show how middlewares work and how you could use them.
You could just trigger the check in a signal when the user logs in and it would still give you the same result.
I stated in the conclusion that this might not be the perfect solution for such feature.
Thanks for your comment and i'm glad you spotted that.
You're really smart.

All the best

Collapse
 
vuthehuyht profile image
Vũ Thế Huy

After log in, I get error 'User' object has no attribute 'user_session'. How to fix this error?

Collapse
 
fleepgeek profile image
Emmanuel Okiche

Could you provide the code where this error points to.
This could be caused by different reasons.
Are you sure you're importing the User model from the right path?
Finally could you show where you used user_session in your code.

Collapse
 
chechonidas profile image
Chechonidas • Edited

Hi! I got this problem when i try to change the user thats is try to log in:

DoesNotExist at /
Session matching query does not exist.

How can I fix this error?

Collapse
 
fleepgeek profile image
Emmanuel Okiche

Hi. I replied on the YouTube video today.

Collapse
 
sangramrajekakade profile image
Sangram Kakade

I got this error 'CustomUser' object has no attribute 'logged_in_user' full trace back dpaste.com/16N37EM

Collapse
 
fleepgeek profile image
Emmanuel Okiche

Hi. I replied to your comment and YouTube but guess you deleted it.
Here you go:

The code snippet you posted has expired but from your error message, it shows you created a CustomUser model and it has no reverse relation with the LoggedInUser model.
Make sure you set the CustomUser model as the OneToOneField for user in your LoggedInUser model.

That could be the possible cause for this because i used settings.AUTH_USER_MODEL for mine. Or you could just set the AUTH_USER_MODEL to your CustomUser model in your settings.py file and your code should work as expected.

Collapse
 
sangramrajekakade profile image
Sangram Kakade • Edited

Thanks For reply,
Now Its Working!!!
thanks for help

Thread Thread
 
fleepgeek profile image
Emmanuel Okiche

You're welcome.

Collapse
 
rmarzo profile image
rmarzo

Can I see views.py file, please?
Thanks a lot!

Collapse
 
fleepgeek profile image
Emmanuel Okiche

I dont have it anymore.
It should work for the default login view.
This is the gist i created: gist.github.com/fleepgeek/92b01d31...

Collapse
 
chirayurathi profile image
chirayu rathi

Does this work with JWT in django rest framework?

Collapse
 
fleepgeek profile image
Emmanuel Okiche

It should work as long as whatever library you're using is using Sessions