Back to Blog

Guide to JWT Authentication in Django

/ Harrison Portwood

JWT, or JSON Web Token, is a compact and self-contained way to securely transmit information between parties as a JSON object. It serves as a token for authentication, allowing parties to verify the information without needing to reference an external source. This makes JWTs ideal for stateless authentication, where checking a database for verification is not feasible.

Django just happens to be a great platform to develop APIs on, and I recently implemented JWT authentication within one of my Django projects. Here is an overview on how I did it.

Dependencies

First, I am going to assume you already have Django up and going. Next, you need to install PyJWT. Since we want to encode our tokens with digital signature algorithms, we also need to install cryptography. We can get both with one line:

Shell
pip install pyjwt[crypto]

The official documentation for PyJWT recommends installing it this way, as a developer might see a separate cryptography requirement line, mistake it as unused and remove it.

Setup

Let's start by installing the necessary dependencies for JWT encoding and decoding. This is a good time to pause and say if you are not sure how encryption/decryption works, then spend some time reading up on it.

Python 3.6 and above comes with the secrets module by default, which makes this pretty easy for us. Let's create a 32 byte hex string:

Python
import secrets
secrets.token_hex(32)

Create an .env file if you don't have one already, and add:

.env
JWT_SECRET={TOKEN_YOU_GENERATED}

Finally, go to your settings.py file and add a line to import this env variable into your project:

Python
JWT_SECRET = os.getenv('JWT_SECRET')

Generating JWTs

Now we are all set up to get working on JWTs. I like to store all my JWT related code in one single file, typically in an utils folder. So my working file path would look something like root/app/utils/jwt.py, but feel free to structure your project however you want. Now, lets write some code:

Python
import jwt
from django.conf import settings
from datetime import datetime, timedelta

def generate_jwt(user):
   payload = {
      'user': user,
      'exp': datetime.utcnow() + timedelta(days=1),
      'iat': datetime.utcnow(),
      'iss': {YOUR_APP_NAME}
   }

   token = jwt.encode(payload, settings.JWT_SECRET, algorithm='HS256')

   return token

Remember, this process occurs right after the user initially authenticates. It is essentially just giving them a key to the house so they don't have to knock on the door each time (or us having to ping the database to verify their claims each time). So the function accepts a user parameter. This could be as simple as a user ID. Or, it could be an object that contains info about the user such as their name, account type, etc...

Our first few lines in the function create the payload for our JWT. While there are no required fields for a JWT, it's a good practice to include these ones by default:

  • iss - Issuer of the JWT
  • iat - When the JWT was issued
  • exp - When the JWT expires

Finally, the last few lines of the function encode the JWT using our secret key we set earlier, and returns it. Now the user has a JWT they can include in each of their API calls that our app can verify against.

Verifying JWTs

We have successfully created a JWT and gave it to the user. Once they make a request to our API, we will need to verify the JWT before returning any data to that user:

Python
import jwt
from django.http import JsonResponse

def verify_jwt(token):
   try:
      payload = jwt.decode(token, settings.JWT_SECRET, algorithms=['HS256'])
      return payload

   except jwt.ExpiredSignatureError:
      return JsonResponse({
         'status': 'error',
         'message': 'Token has expired.'
      }, status=401)

   except jwt.InvalidTokenError:
      return JsonResponse({
         'status': 'error',
         'message': 'Token is invalid.'
      }, status=401)

In this code, we take the JWT as a parameter in our function. We then try to decode it using our secret key. If the token has expired, or we can't validate it, then we throw a JsonResponse that we can return to the user to complete the API call. Otherwise, we return the payload back and continue the request.

Expanding the logic

Congratulations, you have successfully created and verified your first JWT in Django! This is a high-level overview on it, but it's up to you to expand it out as you need. It's probably a smart move to add middleware that automatically verifies the token on each request. Python, with just a few packages, made this a breeze and a great developer experience.