Prevent Multiple Sessions for a User in your Django Application
Tags: python, django, webdev, tutorial
Published: 2018-05-05 16:14:04

---
title: Prevent Multiple Sessions for a User in your Django Application
published: true
description: 
tags: #python, #django, #webdev, #tutorial
---

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 same time.
Whenever a user logs in, the previous sessions is deleted.
This approach could be useful in subscription based apps where you want a single access for the subscribing user.

#Our Approach

For this tutorial, We'll be using django 2.0.4 and its middleware framework.
The middleware is simply the middle guy or lady in between django's request/response processing. Django has some middlewares which we already use in our projects by default. 
The middleware framework is what we're going to use to check if the user has an existing session on our application whenever a user makes a request.
If a session, exists, it deletes it and assigns the new session from the request as the current session. 

**Note**: "Django stores session data in the "django_session" table, so deleting a user's session from the database has the same effect as logging out a user."


#Code Time
Too much talk, right? OK let's just code.
Ermm... one more thing. All the code below should be stored in one app. I believe you know this but there's no harm in stating the obvious sometimes.
Create an app in your django project called 'accounts' and follow this tutorial.

##models.py
We need to create a model to store the list of users that are currently logged into our app

```python
from django.conf import settings

User = settings.AUTH_USER_MODEL

# Model to store the list of logged in users
class LoggedInUser(models.Model):
    user = models.OneToOneField(User, related_name='logged_in_user')
    # Session keys are 32 characters long
    session_key = models.CharField(max_length=32, null=True, blank=True)

    def __str__(self):
        return self.user.username
```

We created a `user` field with a `OneToOneField` so that a `user` can only have one entry in the table. In simple terms, this uniquely identifies a user in the table. In simpler terms, there are no duplicate entries for a user in the table. In the simplest term ['insert yours here']. The `related_name` attribute is used to give a more friendly name for reverse relationship to the `User` model (you'll see soon).
The `session_key` would be used to store the `session_key`. So sorry if you were expecting a simpler explanation. Those times are past.


##signals.py
Create a new file, called `signals.py` in your app directory. Signals are used to broadcast events whenever an action occurs in our app.
For our app, we need to know whenever a user logs in and logs out. With the help of signals, we can know this and perform some actions (here, insert the user to the LoggedInUser table).

```python
# Signals that fires when a user logs in and logs out

from django.contrib.auth import user_logged_in, user_logged_out
from django.dispatch import receiver
from accounts.models import LoggedInUser

@receiver(user_logged_in)
def on_user_logged_in(sender, request, **kwargs):
    LoggedInUser.objects.get_or_create(user=kwargs.get('user')) 


@receiver(user_logged_out)
def on_user_logged_out(sender, **kwargs):
    LoggedInUser.objects.filter(user=kwargs.get('user')).delete()
```

We're using the inbuilt `user_logged_in` and `user_logged_out` signals which fires whenever a user successfully logs in and log outs respectively.
If you were wondering how we got the user' through the `kwargs`, wonder no more my friend. The `user` argument is always sent with the two signals mentioned above.

##apps.py
Add the following code in your `apps.py` file to import your signals. 

```python
from django.apps import AppConfig
class AccountsConfig(AppConfig):
    name = 'accounts'
    # This function is the only new thing in this file
    # it just imports the signal file when the app is ready
    def ready(self):
        import accounts.signals
```

In your `settings.py` file, update your app's name to point to exact app in `apps.py`

```python
INSTALLED_APPS = [
    .....

    'accounts.apps.AccountsConfig',
]
```

##middleware.py
Create a file called `middleware.py` in your app directory. 

```python
#Session model stores the session data
from django.contrib.sessions.models import Session

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:
            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
```
Notice, `request.user.logged_in_user.session_key` we can use `logged_in_user` here because we defined it as the related name for the `user` field in your `LoggedInUser` model. If we didnt do that, the reverse relationship name would be `loggedinuser` which doesnt follow python's naming convention.

Finally, add this middleware we just created in your `MIDDLEWARE` list in your `settings.py` file

```python
MIDDLEWARE = [
    ....

    'accounts.middleware.OneSessionPerUserMiddleware'

]
```

##Conclusion
In this one we learnt how to prevent a user from having multiple sessions in your app. More like preventing bad people from streaming videos on 50 devices (50 friends and family's devices to be precise) from one subscribed account on your web app.

This tutorial was to show you how to create custom middlewares in your django app.
There are definitely other ways which might be better in achieving this. This might not be a perfect solution to your needs. The idea was just to show you how middlewares work.
Now go forth and explore!!
Thanks for reading.

##Video Version
You know what to do after watching this video, right?
OK, let me just state the obvious once more. Hit the 'subscribe' button then click the bell icon. Thanks once again for reading.

{% youtube q7yWy97JISo %}