I'm using python-jose's JWT implementation to generate JWT tokens for authentication purposes.
We're running our backend in a Docker container on Kubernetes and sometimes, when we have multiple pods, we get different tokens for the same claims, secret and algorithm. I've also had this happen on a single container on my development environment when touch
ing my index.wsgi
script.
Pod 1:
>>> jwt.encode({'key': 'value'}, 'secret', algorithm='HS256')
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ2YWx1ZSJ9.FG-8UppwHaFp1LgRYQQeS6EDQF7_6-bMFegNucHjmWg'
Pod 2:
>>> jwt.encode({'key': 'value'}, 'secret', algorithm='HS256')
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJrZXkiOiJ2YWx1ZSJ9.JPIDicqvQ6GAh14yE2yZ3wnZQ0LiLNTTRDtJgLZcn98'
I took a deep dive into the code to see what could be causing this and didn't find anything incriminating. In a nutshell, here's what the code does:
json.dumps
of the algorithm header ({'typ': 'JWT', 'alg': 'HS256'}
) and encode it as Base64, removing any =
'sjson.dumps
of the payload ({'key': 'value'}
) and encode it as Base64, removing any =
'sencoded_header.encoded_payload
using HMAC256 with the secret
key and encode it as Base64, again removing any =
'sencoded_header.encoded_payload.encoded_signature
At this point, I have no idea what is causing this. I'm suspecting a bug of some sort in the HMAC or SHA256 implementation of Python, but that seems rather unlikely... Any clues?
Note: we've successfully reproduced the bug with pyjwt
, which was the base for python-jose
.
This occurs because Python dictionaries are unordered. If you decode the two JWTs, you will see that the header portion is ordered differently for each token.
{
"typ": "JWT",
"alg": "HS256"
}
and
{
"alg": "HS256",
"typ": "JWT"
}
This causes the base64 encoded header to be different, which will in turn cause the signature to be different.
That said, both of those are valid tokens for the exact same claim set, and both should verify successfully. There is nothing in the JWT spec that dictates that an equivalent claim set should result in equivalent JWT output.
Note: I am the author of the python-jose library.