JSON Web Token – (JWT) in python

JSON Web Token (JWT) is a way for securely transmitting information as a JSON object. The information is digitally signed before transmitting.

JSON Web Token structure

JSON Web Tokens consist of three parts (Header, Payload and Signature) separated by dots (.).

Header contains two types of information – the signing algorithm being used and the token type which is JWT. Header contains its information as a json object where signing algorithm is represented by key “alg” and token type is represented by key “typ”.

{
 "alg": "HS256",
 "typ": "JWT"
}

Payload part of JWT token contains the claims. Claims contains the information we want to transmit using JWT token. It may be user information or any kind of information we want to transmit.

 

Assuming that you have an idea about JWT token, We will see how we can generate JWT tokens in python. In this article, We will create a JWT token generator file which you can use in any project. But before that, we will see how a token is actually generated and then we will combine all steps to create token generator file at the end of this article. This article mainly focused on “create JWT token for Django Rest Framework”. So we will start from activating the virtual environment.

Step 1: Activate virtual environment

Step 2: Add jwcrypto in your requirements.txt file

Step 3: install module using pip

pip install -r requirements.txt

Step 4: Generate key

from jwcrypto import jwt, jwk

key = jwk.JWK(generate='oct', size=256)

print(key.export())

Copy exported key from the terminal

Copy the exported key from the terminal. We will use this key to create a signed token. Here is a sample of how your key will look like.

{“k”:”t3aam6I0UXG0mu2gCFBMX8J6RexplM98NyRBqR-eKWg”,”kty”:”oct”}

 

Create a signed token

Now we are going to create a signed token from the exported key. We can create a new key every time to create a signed token to enhance the security. It’s up to your requirement.

expkey = {“k”:”t3aam6I0UXG0mu2gCFBMX8J6RexplM98NyRBqR-eKWg“,”kty”:”oct”}

 

Import an exported Key

key = jwk.JWK(**expkey)

 

Set header and claims data in token. In header, we set the encryption algorithm being used by key parameter “alg”. 

Claims is the payload data/information that token contains. Or in simple words, we can say that whatever payload data we want to encrypt in a signed token, we need to pass that in claims.

Token = jwt.JWT(header={"alg": "HS256"},
                    claims={"info": "I'm another signed token"}
                    )

Now, we are going to create a signed token using the exported key.

Token.make_signed_token(key)
serializedToken = Token.serialize()

Encrypt the serialized signed token with the same key we used earlier.

#encrypt serialized signed token with same key   
Etoken = jwt.JWT(header={"alg": "A256KW", "enc": "A256CBC-HS512"},
                 claims=serializedToken})
Etoken.make_encrypted_token(key)
encryptedToken = Etoken.serialize()
print("\n encryptedToken:", encryptedToken)

 

Your encrypted token will look like this:

eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0.2nZ0TITu6ahB_wVMJtbNogYcOqzM4W8lO9Rtxt8MM4ErvetIvfFFwjWqU8fs1Mx5Q9-Obtric9E_FhtCWYOwTjBNet-T2Cva.wQLexQK6RVldlAv8SIEnbA.NJVDsWVZu5KdV10i-jd7yB7LIr-1lGCZG--PaV9dupljeJN9xGVJ5eoUoJMz2SZR84WNv0NbK30AUNHDwkN5xdejdgrtPR7mEMlZtiIuAZsmGJv4a3ZzxSrno6T_9VU5x4kREbBC5sCG5A1AY14WFkNWZEJdEjnp_yKauMC72H2uMTPOZDJ5O6Y0_NWxcxLc.U7UHVwd6_NyeIE898taAG46az5vnkzB1kW50NTXvRVQ

 

Decrypt the token and get the claimed data

While decrypt the token, we will use the same key we used at the time of generating the token.

 encToken = jwt.JWT(key=key, jwt=encryptedToken)
 serializedToken = jwt.JWT(key=key, jwt=encToken.claims)
 print(serializedToken.claims)

You can see the decrypted information.

{“info”: “I’m another signed token”}

 

Placing them together in a file. Create a file named as “jwtlib.py” in your project’s directory.

touch <PROJECT DIRECTORY>/jwtlib.py

from jwcrypto import jwt, jwk
import datetime

exportedKey = XXXXXXXXXXXXXXXX #replace this with the generated key

def generateKey():
   key = jwk.JWK(generate='oct', size=256)
   print(key.export())

Generate Key

Run generateKey() function to generate the key.

Change your working directory to the path where your file resides.

cd <project directory>

python -c "from jwtlib import generateKey; generateKey();"

Copy the exported key from the terminal and assign it to the variable “exportedKey”. We will access exported key from this variable while we create a signed token and decrypt the token. Preferred way is to save the exported key in .env file or any settings file you are managing in the project. Alternatively, there is a way that you can export the key in .pem file and you can read the exported key from that file. Here are the available options to export the key.

export(private_key=True) – Export the key in JSON format.

export_private() – Export the private key in the JSON format

export_public() – Exports the public key in the JSON format.

export_to_pem(private_key=False, password=False) – You can export public or private key in a .pem file. This is the best encryption method if a password is provided for the file containing the key.

To make it short, we are going to save it in variable “exportedKey“.

 

Now Add a definition to create signed token for the user defined payload.

Note: print statements are added and left uncommented so that you can see what is going on. Remove or comment print statements while you implement the code.

def createSignedToken(payload=None):
   #you can generate and export a new key here if want a different key every time.

   #import an exported key
   expkey = exportedKey
   key = jwk.JWK(**expkey)
   print("\n imported key:", key.export())

   #set expiration time, when expiration time is passed in claims, expiration time will be autochecked while decrypt the token. If token expired than it will throw(automatically) the expired token exception.
   currentTimestamp = datetime.datetime.today().timestamp()
   thresholdTime = 60*10 #60 = 60sec(1 min), 60*10 = 10min
   expTime = currentTimestamp + thresholdTime
   print("\n expTime:", expTime)

   payloadDict = payload

   #add expiration time in user payload dict
   payloadDict['exp'] = expTime

   print("\n\n payloadDict:", payloadDict)

   Token = jwt.JWT(header={"alg": "HS256"},
                   claims=payloadDict
                   )
   Token.make_signed_token(key)
   serializedToken = Token.serialize()
   print("\n serializedToken:", serializedToken)

   #encrypt serialized signed token with same key
   Etoken = jwt.JWT(header={"alg": "A256KW", "enc": "A256CBC-HS512"},
                    claims=serializedToken)
   Etoken.make_encrypted_token(key)
   encryptedToken = Etoken.serialize()

   print("\n encryptedToken:", encryptedToken)
   return encryptedToken

 

Add token expiration

You can see in function createSignedToken() that we pass “exp” variable in claims. “exp” is an optional parameter and represents expiration time. If we want to add a token expiration time, we can pass it in claims. When we decrypt the token, it will auto-check the expiration time and throw token expired exception. You can modify the “thresholdTime” according to your requirement.

Decrypt token

#decrypt token to get the data

def decryptToken(encryptedToken = None):
   try:
       if encryptedToken:
           #import an exported key
           key = jwk.JWK(**exportedKey)

           e = encryptedToken
           ET = jwt.JWT(key=key, jwt=e)
           ST = jwt.JWT(key=key, jwt=ET.claims)
           print(eval(ST.claims))
           return {"status": True, "claims": eval(ST.claims)}

   except Exception as e:
       print(e)
       return {"status": False, "message": str(e) }

If you check the type(ST.claims), It will return string type. So you need to convert it to dict data type using eval().

 

In Views.py

import jwtlib file to create JWT token and decrypt the signed token in views.py file.

#import JWT token lib
from core.jwtlib import createSignedToken, decryptToken
#prepare user payload and create JWT token using userId and gcmId
userPayload = {
              "userId":123,
              "userRole": "guest"
           }
encryptedToken = createSignedToken(payload=userPayload)

Decrypt Token

#decrypt token
decryptData = decryptToken(encryptToken)
print(decryptData)

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to Top
Shares