hckr.fyi // thoughts

Decoding JSON Web Tokens (JWT) in Python Flask Applications

by Michael Szul on

JSON Web Tokens (JWT) have become a growing preference for client-to-server authentication in web applications, and the Auto0 company has a been doing an excellent job championing them as a tool for such light authentication. In fact, Auth0 hosts a great web site that allows you to browse all the different packages from different programming languages that let you encode and decode JWT payloads. It also offers a nice tool for encoding/decoding right in the browser using copy/paste (or you can install their handy Chrome extension).

With Python's popularity over the decades--and the handful of web application frameworks that have been developed over the years--you might want to use JWT authentication in your apps. In this post, we're going to briefly talk about simple decoding using the PyJWT package. We'll cover encoding in a later post.

First, install the package:

pip install PyJWT
    

If you're using Anaconda:

conda install PyJWT
    

Anaconda might force you to upgrade a few other packages.

Once installed, you can import the package:

import jwt
    

…and then it's just a matter of using decode() to decode your JWT. As a quick example, let's say we're working with an application in Microsoft's IIS and this application is a virtual application under the root. The root controls the authentication, including the ability to impersonate other users for troubleshooting, so in addition to the standard Authorization Bearer token you might use for web services, you need a non-database-stored authentication and impersonation to pass through to each virtual application. You could do this with a cookie, and your code might look like this:

if 'my.site.jwt' in request.cookies:
            try:
                decoded = jwt.decode(request.cookies['my.site.jwt'], '<MY SECRET KEY>', algorithms=['HS256'])
                if 'sub' in decoded:
                    user_id = decoded['sub']
            except:
                print('Decoding jwt failed.')
    

In this code, we check for the cookie, and if it's there, we decode the JWT that resides in the cookie. The decode() method takes the JWT, your secret key, and the algorithm used to create the secret key.

Why a secret key? If your JWT is in a cookie or in local storage, it's easily accessible to the end user. They can take that JWT, paste it into JWT.io to decode it, alter it, and put it back into their browser, essentially hacking your JWT, and possibly creating a security hole. If the decoding application uses the secret key that was used to create the JWT, any attempt to hijack the JWT will fail because the decode() method will verify the authenticity of the signature.

If you do not want/need to use a secret key (I recommend that you do), you can also decode the JWT with no verification:

decoded = jwt.decode(request.cookies['my.site.jwt'], verify=False)