A Beginner’s Guide to JWT Authentication in Django REST Framework

Ali Saleh
5 min readApr 15, 2023

--

Photo by Douglas Lopes on Unsplash

Django REST Framework (DRF) is a powerful tool for building APIs in Django. In this tutorial, we’ll walk through the process of setting up JWT authentication with DRF. JWT authentication can provide a secure and stateless way to authenticate users without relying on traditional sessions.

🤔 What is JWT Authentication?

JWT stands for JSON Web Tokens. JWTs are a way of securely transmitting information between parties in a compact, self-contained way. JWTs can be used to authenticate users, among other things.

A JWT consists of three parts: a header, a payload, and a signature. The header and payload are base64-encoded JSON objects, and the signature is calculated based on a secret key.

When a user logs in, they receive a JWT from the server. The JWT contains information about the user, such as their ID and username. When the user makes a request to an authenticated endpoint, they include the JWT in the request header. The server can then verify the JWT’s signature and use the information in the payload to authenticate the user.

🛠 Setting up JWT Authentication in DRF

To use JWT authentication in DRF, we’ll need to install the djangorestframework-jwt package. We can do this using pip:

pip install djangorestframework-jwt

Next, we’ll need to add rest_framework_jwt.authentication.JSONWebTokenAuthentication to the DEFAULT_AUTHENTICATION_CLASSES setting in our Django settings file:

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
}

This tells DRF to use JWT authentication for all API endpoints.

👨‍💻 Creating a Token View

To generate JWTs for users, we’ll need to create a token view. This view will take a username and password and return a JWT if the user is authenticated.

We can create a new view in one of our app’s views files:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework_jwt.settings import api_settings
from rest_framework_jwt.views import ObtainJSONWebToken

class TokenObtainView(ObtainJSONWebToken):
def post(self, request, *args, **kwargs):
response = super(TokenObtainView, self).post(request, *args, **kwargs)
token = response.data['token']
return Response({'token': token})

obtain_jwt_token = TokenObtainView.as_view()

This view extends the ObtainJSONWebToken view from DRF's JWT authentication package. We override the post method to return just the token instead of the full response.

We’ll also need to add a URL pattern to our app’s urls.py file to map to this view:

from django.urls import path
from . import views

urlpatterns = [
path('api-token-auth/', views.obtain_jwt_token),
# Other API endpoints go here
]

This will create a URL pattern for our token view at /api-token-auth/.

💻 Testing Authentication

Now that we’ve set up JWT authentication, let’s test it out. We can use a tool like Postman to make requests to our API.

First, let’s generate a JWT for a user. We can make a POST request to the /api-token-auth/ endpoint with a username and password in the request body:

POST /api-token-auth/
Content-Type: application/json

{
"username": "user1",
"password": "password123"
}

This should return a JSON object with a token key.

Once we have a JWT, we can use it to authenticate requests to our API. We’ll need to include the JWT in the Authorization header of our requests.

GET /api/endpoint/
Authorization: JWT eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJ1c2VybmFtZSI6ICJ1c2VyMSIsICJleHAiOiAxNjMwNjcyMjk3fQ.AxrLq3jKQnGvaDjRvRlWnX8CvxJzybrjD6wQUQ6bhgI

This should return a response with the requested data. If the JWT is invalid or expired, the server will return a 401 Unauthorized response.

🤝 Refreshing Tokens

By default, JWTs have a short lifespan, usually around 15–30 minutes. After this time, the user will need to log in again to generate a new token.

To avoid this inconvenience, we can implement token refreshing. This allows users to refresh their JWT without logging in again.

To enable token refreshing, we’ll need to add rest_framework_jwt.authentication.JSONWebTokenAuthentication to the DEFAULT_AUTHENTICATION_CLASSES setting in our Django settings file, along with rest_framework_jwt.serializers.RefreshJSONWebTokenSerializer and rest_framework_jwt.views.RefreshJSONWebToken.

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
}

JWT_AUTH = {
'JWT_ALLOW_REFRESH': True,
'JWT_REFRESH_EXPIRATION_DELTA': timedelta(days=7),
'JWT_AUTH_HEADER_PREFIX': 'Bearer',
'JWT_AUTH_COOKIE': None,
'JWT_AUTH_TOKEN_CLASSES': (
'rest_framework_jwt.tokens.AccessToken',
'rest_framework_jwt.tokens.RefreshToken',
),
}

The JWT_ALLOW_REFRESH setting allows token refreshing. The JWT_REFRESH_EXPIRATION_DELTA setting determines how long a refresh token is valid. The JWT_AUTH_HEADER_PREFIX setting specifies the prefix for the Authorization header. The JWT_AUTH_COOKIE setting specifies the name of the cookie used for authentication. The JWT_AUTH_TOKEN_CLASSES setting specifies the types of tokens that can be used for authentication.

We’ll also need to create a view for refreshing tokens:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework_jwt.settings import api_settings
from rest_framework_jwt.views import RefreshJSONWebToken

class TokenRefreshView(RefreshJSONWebToken):
def post(self, request, *args, **kwargs):
response = super(TokenRefreshView, self).post(request, *args, **kwargs)
token = response.data['token']
return Response({'token': token})

refresh_jwt_token = TokenRefreshView.as_view()

This view extends the RefreshJSONWebToken view from DRF's JWT authentication package. We override the post method to return just the token instead of the full response.

We’ll also need to add a URL pattern to our app’s urls.py file to map to this view:

from django.urls import path
from . import views

urlpatterns = [
path('api-token-auth/', views.obtain_jwt_token),
path('api-token-refresh/', views.refresh_jwt_token),
# Other API endpoints go here
]

This will create a URL pattern for our token refreshing view at /api-token-refresh/.

To refresh a token, we can make a POST request to the /api-token-refresh/ endpoint with the old token in the Authorization header:

POST /api-token-refresh/
Authorization: JWT eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJ1c2VybmFtZSI6ICJ1c2VyMSIsICJleHAiOiAxNjMwNjcyMjk3fQ.AxrLq3jKQnGvaDjRvRlWnX8CvxJzybrjD6wQUQ6bhgI

The response should include a new JWT:

HTTP 200 OK
Allow: POST, OPTIONS
Content-Type: application/json
Vary: Accept

{
"token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJ1c2VybmFtZSI6ICJ1c2VyMSIsICJleHAiOiAxNjMwNzA1NjE1fQ.BhnwBQokxTULjQd0Byr6LTYj1R43G64_LJ0DmBodmZM"
}

We can then use this new token to authenticate future requests.

👋 Conclusion

JWT authentication is a powerful tool for securing Django REST Framework APIs. With just a few configuration changes, we can implement token-based authentication and authorization for our API endpoints. By understanding the basics of JWTs and their usage in DRF, we can build more secure and scalable web applications.

🎉 Thanks for reading

Hey there — I hope you found this article informative and helpful. If you have any questions or comments, please feel free to reach out to me via email (alisheikh1114@gmail.com). I am always eager to connect with readers and help them with any technical challenges they may be facing.

👨‍💻 Let’s connect on social media

Don’t forget to follow me on Medium if you are interested in reading about Web Dev Stacks, Big Data, and Open Source.

Find me online: GitHubLinkedInTwitterFacebookInstagram

🤝 Share the knowledge

If you found this article useful, please consider sharing it with your friends and colleagues who might benefit from it. Sharing is caring, and it helps spread knowledge and insights to those who need it the most.

--

--

Ali Saleh

🎉 Tech Enthusiast ✨ Full Stack Developer 💙 Web / Big Data / Open Source ~ Find me online: https://www.linkedin.com/in/iamalisaleh