JSON web token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON web signature (JWS) structure or as the plaintext of a JSON web encryption (JWE) structure. This enables the claims to be digitally signed or integrity protected with a message authentication code (MAC) and/or encryption.
What Is JWT?
JSON web token (JWT) is a secure means of representing claims transferred between two parties, often a client and server. Claims are encoded as a JSON object containing a set of claims and a signature. It can be decoded in Python using multiple libraries, including python-jose
and PyJWT
.
In other words, JWT is an open standard used to share information between two parties securely — a client and a server. In most cases, it’s an encoded JSON containing a set of claims and a signature.
Python provides multiple libraries to encode and decode JSON web tokens. Let’s look at a couple of these libraries:
How to Encode JWT Using PyJWT
Let’s start with using PyJWT
as the library.
Encode
import jwt
def encode_user():
"""
encode user payload as a jwt
:param user:
:return:
"""
encoded_data = jwt.encode(payload={"name": "Dinesh"},
key='secret',
algorithm="HS256")
return encoded_data
if __name__ == "__main__":
print(encode_user())
Output
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiRGluZXNoIn0.7Fwj-RvoEP2-LfB5q05pdTvMl7pFpoQgwXYq3EOLens
Process finished with exit code 0
The code above is self-explanatory. We have a payload as a Python dict
, i.e. the data to be encoded, and a secret key used to encode. Also, we have used the algorithm HS256 for signing the token. The algorithms supported by PyJWT are provided on its site.
How to Decode JWT
Let’s try and understand the structure of a JWT token. A JWT token typically contains a user’s claims. These represent data about the user, which the API can use to grant permissions or trace the user providing the token. The different components of a JWT token are separated with a period(.). A JWT token consists of three parts. Each section is comprised of base64url-encoded JSON containing specific information for that token:
- Header
- Payload
- Signature
jwt.io can also be used to decode a JWT token, breaking it into the components above.
Decode
import jwt
def decode_user(token: str):
"""
:param token: jwt token
:return:
"""
decoded_data = jwt.decode(jwt=token,
key='secret',
algorithms=["HS256"])
print(decoded_data)
Output
/Users/dkb/VirtualEnvs/py3.11-env/bin/python3.11 /Users/dkb/Code/practice/my_jwt.py
{'name': 'Dinesh'}
Process finished with exit code 0
How to Decode a JWT Using a Public Key
A public key can be used to decode a JWT. Usually these public keys should be made available to tenants using the uniform resource identifier (URI) format below. Every open ID server has to provide this endpoint. In our case, the public key is called as a JSON web key (JWK).
JWK is a JSON object that contains a well-known public key that can be used to validate the signature of a signed JWT.
If the issuer of your JWT used an asymmetric key to sign the JWT, it will likely host a file called a JSON web key set (JWKS). The JWKS is a JSON object that contains the property keys, which in turn holds an array of JWK objects.
https://--YOUR DOMAIN----/.well-known/jwks.json
Sample Response:
{
keys: [
{
alg: 'RS256',
kty: 'RSA',
use: 'sig',
n: 'tTMpnrc4dYlD8MtmPnW3xZNbLxkaGCUwTqeKB4dfLg11dEpMyQEc4JRxUvRzp9tz00r6lkZ1ixcvIiuB_eMVckU8VyFSFWBSAxp5duBk6lRpYk-QjK3kEdPxYLxyW84gNzwMi-XW8zxJbsOa-cRM9sCb62Qz2yfWoQfimoFXsCnVHq496kizO7gZ972JefvTce1_n9dd_1p0K6c14qcCXtF6hbA_gQ0N7h3IyloBqiusKyTsV-ZrMZDldZkI-4v7s49TdcRZgEOvSapMz5YyoDvAWzuWGEiljkjkCOo0Mr5Sioi2x0dBm6nJ2WVYfZrwEF5J',
e: 'AQAB',
kid: 'NTY2MjBCNzQ1RTLPQzk3NzczRRTMQ0E4NzE2MjcwOUFCRkUwRTUxNA',
x5t: 'NTY2MjBCNzQ1RTJPLzk3NzczRUNPO0E4NzE2MjcwOUFCRkUwRTUxNA',
x5c: [Array]
}
]
}
Now, let’s write a Python code to decode a JWT token using python-jose
.
import jwt
import httpx
def decode_access_token(authorisation_token):
# get public key from jwks uri
response = httpx.get(url="your open id wellknown url to fetch public key")
# gives the set of jwks keys.the keys has to be passed as it is to jwt.decode() for signature verification.
key = response.json()
# get the algorithm type from the request header
algorithm = jwt.get_unverified_header(authorisation_token).get('alg')
user_info = jwt.decode(token=authorisation_token,
key=key,
algorithms=algorithm)
return user_info
I used python-jose here just to show that there is no significant difference between these libraries. python-jose
is a wrapper on top of PyJWT
.
The reason I have mentioned both libraries is that sometimes your build pipeline like Gitlab/Jenkins complains about having incompatible versions of cryptography with PyJWT
. However, python-jose
offers a quick solution in those scenarios without requiring you to change the code.
And the code doesn’t look much different from PyJWT
, does it?
With all this in mind, remember that anyone can decode the information contained in a JWT without knowing the private keys. For this reason, you should never put secret information like passwords or cryptographic keys in a JWT.