Role Based Authentication and Authorization with DjangoRestFramework and SimpleJWT

Authorize your users to access endpoints based on their roles

Brock Joseph Herion
Python in Plain English

--

Authentication and Authorization are two difficult concepts in programming. Lucky for us, Django makes our lives super easy and handles those things for us.

Recently, I have been working on a Django REST API that has around seven roles in it, all have different permissions for both the back-end API and the web app, which displays different views based on roles. This created a problem, as Session Authorization is great until we start working with mobile apps, which is the next step in this application. A JWT token was needed for authentication, but how was I to handle authorization? Let’s take a look at how to add role-based authorization to your Django REST apps.

Project Setup

You should have Python already installed on your system. Let’s create our virtual environment and get Django installed.

First, create the directory for your Django project and cd into it:

mkdir my-django-app && cd "$_"

Next, we need to create our virtual environment:

// macOS and Linux
python3 -m venv venv
// Windows
python -m venv venv

Active your environment with:

// macOS and Linux
source venv/bin/activate
// Windows
.\venv\Scripts\activate

And finally, install your dependencies:

// macOS and Linux
pip3 install django djangorestframework djangorestframework-simplejwt
// Windows
pip install django djangorestframework djangorestframework-simplejwt

Freeze your requirements with:

// macOS and Linux
pip3 freeze > requirements.txt
// Windows
pip freeze > requirements.txt

Now we can start writing some code!

Creating your Django App

Now we are ready to start our Django project. Start with:

django-admin startproject my_django_app

This will create a new folder in your current directory with a folder of the same name and a file named manage.py inside of it. You need to move those files to your current directory. Once you do that, you can remove the original folder that was created. The folder structure should like like the following:

Next, let’s create an app. Run the following command to start a project:

// macOS and Linux
python3 manage.py startapp api
// Windows
python manage.py startapp api

Create Your Model and Manager

We are going to create a custom user for our authentication needs. Add in the following code below to create our user:

Then, we can add fields to our user:

The code above is just normal Django code. I replaced the default username field with an email address and I also removed is_staff and is_superuser. These fields are fine to have if you have a use for them. In this example, we don’t have any. We are also setting the default role of a ‘Employee’ for all new users in our application.

You may have noticed I also added an extra UUID field called uid to the model, but not at the primary key. I like to use a UUID as a public identifier that we can render in a URL. I only put it on resources that will be displayed on their own, not internal or nested tables. This way, I still have an integer field as my PK that I can easily do queries on in the database and I have a safer identifier to use in my URL’s.

A user manager class allows you to write your own logic for creating users and superusers. Add a managers.py file to your api app and add the following code to it:

Notice here when I create my superuser, I am setting my role to 1, or ‘Admin’. In this case, I want to make a super user when I create an admin, but you can set this field to whatever role you want. Again, this is just standard Django code.

Update settings and Make Migrations

Now we need to go into our settings.py file and add a couple of fields. We first need to add our new app to the INSTALLED_APPS and setup our settings for authentication and for the JWT.

These settings tell Django to use the JWT token as the default authentication schema. The settings for the JWT token are the default settings from the SimpleJWT docs.

Next, we need to run our database migrations. Use the following command to run your migrations:

// macOS and Linux
python3 manage.py makemigrations
// Windows
python manage.py makemigrations

Then to apply the migrations:

// macOS and Linux
python3 manage.py migrate
// Windows
python manage.py migrate

We are now ready to start building our serializers.

Creating Serializers

In Django Rest Framework, a serialzier is used to take complex data and put it into a standard Python data form to be used as JSON, XML, or other content types. Let’s create a serializer for our registration process.

All we need to register a user is their email address and a password.

Now, we need a serializer to handle our logins. Here, we need to authenticate a user based on a given email and password and return the JWT back to the user.

The login serializer is pretty straightforward. We are defining all required fields for handling login requests. Note that password is write_only. This is to prevent a password in plain text from being returned back to the user as apart of the response. We also have an access and refresh token. The access token lets the user access resources and expires after five minutes by default. When it expires, we send the refresh token back to the server and have it return another access token. More on that in the SimpleJWT docs.

Finally, we need one more serializer to handle returning all users data. This object can only be accessed by Admins. This is the route we will use our role authorization for.

This serializer is really basic. We just want to get the email addresses and user roles back from the API.

Creating Views, Update URLs, and Run Tests

We need to create three views. One for registration, one for login, and one for accessing all the users.

Let’s first create a registration view:

Now, we need to create a view for logins:

Finally, we need a view for our list of users:

Here, we are setting the permissions classes to IsAuthenticated. This stops users who aren’t logged from accessing this information. Next, we need to check the users role. We can get that from request.user.role. If the role is 1 or is an admin, then they can access the requested resource.

Now we need to update our URLs to be able to hit our endpoints. Create a urls.py file and add the following code:

We now need to import them to our main urls.py file:

Let’s add some tests to see if it works. Open up test.py and add the following code.

All the tests should run successfully.

In this guide, we looked at how to implement role based authorization in Django REST views with JWT authentication. We created a custom user object and tied a role to it, as well as secured our routes. We also wrote unit tests to test our views.

One thing that could be expanded on is if there is a need for multiple roles, you could create a separate role table and create a many to many relationship between that and your custom user object. This would need a little more detailed serializer, but as a whole, is completely doable. It all just depends on what you need in your application.

Full project can be found here: https://github.com/BrockHerion/django-rest-role-jwt

--

--

I am a software developer who love coding in Python, Javascript, and C#. I am a sucker for learning new technologies and tooling to make development easier.