9.15 Working with JWTs

When you request an access token from the web.oauth2 web service, it is returned as a JSON Web Token (JWT). This is a standard format that provides information about the token, including its expiry date.

You can inspect a token using standard tools; for example, you can use the jwt.io website.

You can also inspect a token using a variety of libraries. In this example, you can use the Python jwt library.

9.15.1 Creating your JWT script

Create a new file in the same folder as settings.py and access_token.py and name it:

11-jwt.py

Add the following imports:

Copy
import access_token
import DPAPI
import settings
import requests
import datetime
import jwt
from jwt import PyJWKClient

This example requires the datetime module to process the expiry date, as well as jwt to decode the JWT and PyJWKClient to verify the JWT signature. You must also have the cryptography module installed if you want to verify the digital signature:

py -m pip install pyjwt

py -m pip install cryptography

Add the usual settings and access code:

Copy
# Get the settings from the settings.py file
server = settings.server
client_id = settings.client_id
encrypted_secret = settings.encrypted_secret
client_secret = DPAPI.decrypt(encrypted_secret)
scope = settings.scope

# Get the access token
token = access_token.getAccessToken(server, client_id, client_secret, scope)

9.15.2 Decoding the JWT

You can now decode the access token JWT.

Add the following code:

Copy
# Decode the JWT
decoded = jwt.decode(token["access_token"], options={"verify_signature": False})

# Obtain the expiry time from the decoded JWT
print("\nExpiry time decoded from the JWT: ")
print(datetime.datetime.fromtimestamp(decoded["exp"]))

This uses the jwt module to decode the token without verifying its signature. It then displays the expiry date from the token.

If you want to view all the details of the decoded JWT, use the following:

Copy
print(decoded)

You can run the script now.

9.15.3 Verifying the signature

You can also decode the access token JWT while verifying the signature. This takes a few more steps to obtain the signing key.

Add the following code:

Copy
# Obtain the JWT signing key
response = requests.get("https://" + server + "/web.oauth2/.well-known/openid-configuration")
jwks_uri = response.json()['jwks_uri']
jwks_client = PyJWKClient(jwks_uri)
signing_key = jwks_client.get_signing_key_from_jwt(token["access_token"])

# Decode the JWT, validating the signing key
try:
    data = jwt.decode(
        token["access_token"],
        signing_key.key,
        algorithms=["RS256"],
        audience="myid.rest",
        options={"verify_exp": True},
    )
    # Obtain the expiry time from the decoded JWT
    print("\nExpiry time decoded from the JWT with signature validation: ")
    print(datetime.datetime.fromtimestamp(data["exp"]))
except Exception as e: print(e)

How does this code work?

First you need to find the location of the JSON Web Key Set (JWKS), which is the set of keys that you can use to verify the JWT. You can obtain this from the configuration of the web.oauth2 server:

http:://<server>/web.oauth2/.well-known/openid-configuration

The script pulls the URI of the JWKS from this configuration information, then creates a Python JWK client from the URI. It then obtains the signing key from this client.

The script can then decode the JWT, like the previous section, but this time passing in the signing key. If the signing key is valid, it displays the expiry time.

9.15.4 Troubleshooting

If you see an error similar to:

Signature verification failed

this means that the script could not verify the signature. It is possible that the JWT was created from a different server. This is unlikely to happen in this script, but it is a case you must consider when verifying the signature of a JWT in your own projects.