<p>I'm using <a href="https://github.com/mpdavis/python-jose" rel="nofollow noreferrer">python-jose</a>'s JWT implementation to generate JWT tokens for authentication purposes.</p>

<p>We're running our backend in a Docker container on Kubernetes and <strong>sometimes, when we have multiple pods, we get different tokens <em>for the same claims, secret and algorithm</em></strong>. I've also had this happen on a single container on my development environment when <code>touch</code>ing my <code>index.wsgi</code> script.</p>

<p>Pod 1:</p>

<pre><code>&gt;&gt;&gt; jwt.encode({'key': 'value'}, 'secret', algorithm='HS256')
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ2YWx1ZSJ9.FG-8UppwHaFp1LgRYQQeS6EDQF7_6-bMFegNucHjmWg'
</code></pre>

<p>Pod 2:</p>

<pre><code>&gt;&gt;&gt; jwt.encode({'key': 'value'}, 'secret', algorithm='HS256')
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJrZXkiOiJ2YWx1ZSJ9.JPIDicqvQ6GAh14yE2yZ3wnZQ0LiLNTTRDtJgLZcn98'
</code></pre>

<p>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:</p>

<ol>
<li>Do a <code>json.dumps</code> of the algorithm header (<code>{'typ': 'JWT', 'alg': 'HS256'}</code>) and encode it as Base64, removing any <code>=</code>'s</li>
<li>Do a <code>json.dumps</code> of the payload (<code>{'key': 'value'}</code>) and encode it as Base64, removing any <code>=</code>'s</li>
<li>Sign <code>encoded_header.encoded_payload</code> using HMAC256 with the <code>secret</code> key and encode it as Base64, again removing any <code>=</code>'s</li>
<li>Concatenate the signature to the previous string, resulting in <code>encoded_header.encoded_payload.encoded_signature</code></li>
</ol>

<p>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?</p>

<p>Note: we've successfully reproduced the bug with <code>pyjwt</code>, which was the base for <code>python-jose</code>.</p>

<p>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.</p>

<pre><code>{
    "typ": "JWT",
    "alg": "HS256"
}
</code></pre>

<p>and </p>

<pre><code>{
    "alg": "HS256",
    "typ": "JWT"
}
</code></pre>

<p>This causes the base64 encoded header to be different, which will in turn cause the signature to be different.</p>

<p>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.</p>

<p>Note: I am the author of the <a href="https://github.com/mpdavis/python-jose" rel="noreferrer">python-jose</a> library.</p>
