JWT Authentication in Django, Part 1: Implementing the Backend

John Kealy
Mar 11 · 7 min read

This article walks through the implementation of JWT authentication using a Django backend with an independent frontend, such as React or Vue. Since this topic is at a more intermediate level, a little knowledge of the following is assumed:

  • Django
  • Django Rest Framework

If you’d like to jump straight to Part 2 (all things Frontend) click here.

JWT Authentication, a controversial topic?

Authentication (along with its little brother, authorization) is an essential component of the modern web. That’s why I was so surprised to find such controversy still existing around it. If you dig in, you’ll find blogs and articles that seem to contradict each other, and JWT is often on the sh*t-list.

The reason I wrote this article was to showcase what I learned when trying to set up authentication in my decoupled Django project, hopefully saving you some pain. The goals I wanted to accomplish were the following:

  • Set up authentication/authorization in a new or pre-existing project, powered by a stand-alone Django Rest Framework backend and a Vue.js frontend.
  • Host the frontend at an apex domain (e.g. ) and the backend at a subdomain (e.g. )
  • Use JWT (JSON Web Tokens) as the authentication method
  • Not throw my laptop out the window

So why is JWT authentication controversial?

Some developers will simply tell you not to use JWT, despite its popularity. I think a lot of this hating on JWT stems from the fact that it is stateless, meaning that if an attacker can get hold of your access token, the server will trust it no matter where it originated. Cross-site scripting (XSS) is a key vulnerability here, one that is less of a worry when using session authentication. On the other hand, it’s not uncommon for devs to implement JWT without really needing to do so, and session authentication is a safe and reliable alternative.

Many blogs and tutorials explaining JWT will casually tell the reader to just go ahead and store the access tokens in Local Storage, or cookies. Since Javascript can access these, bad things can happen.

Anyway, the point of this article is not to debate JWT, it assumes you’ve decided JWT is the best choice for your use case. This article will explain how to implement it. Part 1 will focus on the the backend, followed by a review of the frontend in Part 2.

Django authentication libraries

There are tons of authentication libraries for Django. Many do the same or similar things. Some are not well maintained, some are incredibly well maintained and documented. If you want to do some homework, here are my suggestions to look at:

At some point, you’ll probably come across OAuth,OAuth2, and OpenID Connect as well. These are deeper topics. Just keep in mind that

OAuth is an open standard for access delegation, commonly used as a way for Internet users to grant websites or applications access to their information on other websites but without giving them the passwords.

So OAuth is a standard, and you can use JWT within that standard. There’s also a third party service called Auth0 which I was constantly confusing with OAuth… so watch out for that one. Of course, if you want to look into third party tools, feel free to check out Auth0 and Okta.

All things considered, I chose Dj Rest Auth. This was the one I found to best fit my preference for the library to “just work”. And in actual fact, you can configure it to use the Simple-JWT and All-Auth libraries anyway, so it all ties together nicely. Plus, the documentation can take you into using social authentication (e.g. Facebook or Github logins) when you’re ready to do so.

So without further ado, let’s do some coding.

Setting up the Django backend

I won’t go into the basics of how set up a Django application, this is assumed. Nor will I go into the very simple installation of ; I trust you to follow the excellent documentation.

After installing (don’t forget to apply the migrations) you will immediately have access to various REST endpoints such as and . It’s a good idea to test these out in the Django Rest Framework browsable API (which normally lives at ).

If you hit errors at this stage, make sure you’ve added all the necessary libraries in , and installed everything into your virtual environment.

INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.admin',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_extensions',
'corsheaders',
'dj_rest_auth',
'rest_framework',
'rest_framework.authtoken',
'django.contrib.sites',
'allauth',
'allauth.account',
'allauth.socialaccount',
'dj_rest_auth.registration',
'sslserver', # we'll discuss this one later
'users' # This is just the name of my django app
]

Notice that this list includes social accounts; this is actually so that the registration endpoint of can work. Also, be sure to have installed in your virtual environment.

Okay, now we’ll explicitly start using JWT. The settings to apply in are:


...
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'dj_rest_auth.jwt_auth.JWTCookieAuthentication'
),
'DEFAULT_SCHEMA_CLASS': \
rest_framework.schemas.coreapi.AutoSchema',
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated'
]
}
REST_SESSION_LOGIN = False
REST_USE_JWT = True
JWT_AUTH_COOKIE = 'jwt-access-token' # you can set these
JWT_AUTH_REFRESH_COOKIE = 'jwt-refresh-token' # to anything
JWT_AUTH_SECURE = True
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOWED_ORIGINS = ['https://example.com']
...

This way, we’ve turned off session authentication, told Django to use JWT, set the names of the cookies we’ll send to the browser, set CORS to accept requests with embedded credentials, and finally, set https.

The last point creates a problem: how are we supposed to use SSL/TLS in a development environment? Sure, we could just not set , but it turns out that we’re going to need https anyway. Which leads us to…

Cookies (nom nom)

Okay, back the controversy. I think people mostly seem to hate on JWT because developers keep placing the access token and the refresh token in LocalStorage. Don’t do this. And don’t put them in regular cookies either.

What needs to be done is to put the tokens in a special type of cookie called a httpOnly cookie. Javascript can’t read this type of cookie, which offers some protection against XSS.

Luckily, abstracts nearly everything about httpOnly cookies (well, maybe not that luckily, I picked the library pretty much because of this). What isn’t covered, however, is exactly how to make a decoupled frontend application interact with the backend using these cookies. There are important differences that now arise that you didn’t need to worry about when just using the browsable API:

  • ’s httpOnly cookies like https
  • The domains must be ‘same-site’

What these points boil down to is that we need https, even in our development environment, and we must use proper domain names that are related i.e. subdomains. We need to spoof these to in order to use our development environment. In production, this won’t be an issue because we’ll have SSL and real DNS.

Note: I don’t think I need to tell you to use https in production. If you find Nginx a little confusing, look into CaddyServer, it’s https by default.

How to create a https dev environment with custom domains

On the backend, there’s a tool for this: . Install it into your environment, add it to , and replace with . Now just tell your browser that it’s okay to accept the self signed certificate, and you’re good to go. Magic.

We also need our authentication backend to see the API requests as coming from the same root/apex domain for it to work right. This can be achieved by applying local domains in your hosts file. In Linux, this is located at .

Add the following to , or whatever the hosts file is for your operating system:

127.0.0.1     api.example.com
127.0.0.1 example.com

We still need to include the ports, but you should now be able to visit the browsable API at , and the frontend at (or whatever port your frontend is listening on).

Final Backend Task: A splash of custom middleware

Okay, we’re very nearly done with the backend. However, there’s a sticking point about , as discussed on this Github issue tracker entry. Hopefully it will be solved in a later release, but for now, the problem is that our browser is going to be sending our access tokens in the request header.

This is fine for normal requests, but if we wish to refresh our access token, requires that the refresh token be sent in the body, not the headers. The following middleware was suggested in the Github issue to move the tokens from the header to the body.

In your Django app, create a file. Add this code:

You must then add this middleware to :

MIDDLEWARE = [        ...    'yourappname.middleware.MoveJWTRefreshCookieIntoTheBody',
]

With all this set up, Django should now be running a functioning JWT server in your development environment.

In Part 2 of this article, we’ll explore some frontend settings, and round off the full stack implementation.

Geek Culture

Proud to geek out.

Sign up for Geek Culture Hits

By Geek Culture

Subscribe to receive top 10 most read stories of Geek Culture — delivered straight into your inbox, once a week. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store