djwto: Django Authentication with JWT

Introducing an alternative implementation for the auth layer using JWTs.

Will Fuks
Geek Culture
6 min readJul 6, 2021

--

I usually write about data science projects. So it begs the question: “How come this post can be about adding a JWT based auth layer on top of Django?”

Well, what I can say is, on each project that we work on, the need to offer customers some sort of a simple and yet effective online portal where they can interact with the ML models became quite clear.

And that’s basically the scenario that sets my journey on developing djwto (“jot two”) which stands as an alternative JWT auth implementation for Django. One of the steps for building the front-end is a proper auth layer and we chose using JWTs for doing so.

There’re currently already plenty of great packages out there, djwto basically adds some new features whilst aiming for simplicity and being lightweight. Among some new possibilities:

  • djwto offers the option of splitting the access token in two parts (that’s the main inspiration for its name) where one piece is the decoded payload.
  • Works with either bearer tokens or cookies.
  • CSRF protected by default.
  • Offers decorators for protecting views (full authentication and authorization layers).
  • Fully customizable.

In this post, let’s see a brief introduction of djwto and how it can help developers to add the auth layer on their projects as well. We’ll build a Django project and interact with some of the aforementioned features along the way.

1. The Environment

We first need a Django project to begin with so let’s create one (for what follows it’s assumed a unix based system is used).Change directory to one that is empty and run the commands:

Feel free to use another version of Python (it can be either 3.7 or 3.9); this is what we have so far:

Now let’s modify urls.py to include djwto’s urls:

It’s also possible to modify settings.py to configure how djwto should run. Here’s a simple example:

First we add "djwto" to INSTALLED_APPS (here "sslserver" has been added as well just for testing with https, it’s not necessary in practice).

Each setting value should be intuitive for the most part. A few of them is worth further discussing:

  • DJWTO_SIGNING_KEY should contain the key for encoding the tokens and by default is expected to be available as an environmental variable. If using asymmetrical cryptography, then DJWTO_VERIFYING_KEY should also be set.
  • DJWTO_MODE sets how the JWTs will be handled to the client. djwto works with two tokens: access and refresh. They are essentially the same thing but the former is short-lived and should be “refreshed” by using the latter, which in turn is long-lived. The mode of operation can be one of the following:
    - JSON: The JWTs are returned to the client as regular JSONs.
    - ONE-COOKIE: The JWTs are stored in cookies.
    - TWO-COOKIES: Similar to before but this time the access token is split into two parts. One is not encoded and therefore its payload is accessible by the client.

Further explanation for each setting is available in the official docs.

A user will also be required in the database; feel free to create one using your favorite method. Let’s run the migrations first:

And then let’s use the shell :

Run the following:

Now the database contains the user alice which we’ll be using for the auth verification step.

Finally, let’s create a new app for our project where we’ll interact with djwto. Just run in your terminal:

Let’s put djwto to the test now!

2. Running djwto

To begin with, let’s use the requests library and send a login POST request with the user alice to see how it goes. First we need to run the server:

Here we exported the value of the signing key and used sslserver for the https encryption management, all that on port 8002 (trying to pick a somewhat unusual port).

2.1 Login

Here’s the Python code to send a login request

Now if the mode at with djwto is running is JSON then the result should be both access and refresh tokens being retrieved in the response:

refresh and access tokens returned in JSON mode.

Let’s go ahead and change the settings at which djwto is running and set its mode to the TWO-COOKIES option:

The same code now will return something different. The sess variable will contain cookies with csrftoken and the JWTs:

jwt_access_payload contains a base64 encoded value of the token payload. In fact, in order to retrieve the original value, just decode it:

Decoding the access JWT token.

This value can be used directly by the front-end client (still be careful to not store sensitive information there).

Let’s see now how we’d perform token validation to confirm the JWTs are still good.

2.2 Validate

Given that the tokens were created, at times it’s desired to check if they are still valid at some point. Suppose we are in JSON mode:

Notice the token is simply added to the header AUTHORIZATION following the Bearer pattern. This should return:

Valid response

For ONE-COOKIE or TWO-COOKIES modes, here’s how to do it:

Notice the csrf token is sent in the header X-CSRFToken as well as a REFERER indicating from where the request came from (this is necessary due to Django’s security system).

Validating the refresh token is a bit different:

The rest of all endpoints are fully documented in the official docs.

3. Protecting Views

djwto offers direct protection of views by requiring the JWTs being available in the input request. For testing that, let’s create a view in our testapp like so:

Notice the @method_decorator(auth.jwt_login_required) and auth.jwt_perm_required. When decorating views with those, the view will only be processed if the request contains an authenticating JWT.

Create a file urls.py in testapp so we can route to those views, like so:

Update the project urls file to include the newly added testapp:

Now we can send the GET request to our server:

Which returns: "worked!" . If we remove the JWTs from the request, here’s what happens:

View is fully protected by the JWTs.

Each view containing the jwt_login_required is fully protected now; the same can be used for user permissions.

4. Customization

djwto was built aiming for being customizable. Let’s see an example. Using our testapp, let’s update the files apps.py to change how the users are processed when creating the tokens:

Notice we import djwto.tokens and change the function that processes the users. Now, when the user logs in, a new field email will show up in the tokens; you can change its behavior for better suiting your needs. Here’s an example result of a new token:

5. Conclusion

This was djwto in a nutshell. Many other features are fully documented in the official repo:

There you’ll find more about signals, endpoints, customizations, managing the two-cookies settings and much more. Feel free to check it out!

--

--

Will Fuks
Geek Culture

Mainly interested in data science and software development topics.