JWT(JSON Web Token)を利用する機会があったのですが、公開鍵による検証はともかくデータ内容を確認する必要があり、データによってはお客様の情報が含まれてしまうことから、自作することにしました。
公式サイトによると、JWTの構成は以下のように記載があります。
- Header, Payload, Signatureの3つで構成され、それらが .(ドット) で連結されている
- Headerは、主にトークンの種別と署名アルゴリズムの2つのキーで構成されたJSONで、Base64Urlでエンコードされている。
- Payloadが今回確認したいデータ部分で、これもBase64Urlでエンコードされている。
- Signatureは利用しないので割愛。
つまり、フォームで受け取ったデータをsplit(".")してBase64でデコードし、整形したJSONを表示すればよいと考えたのですが、なかなかうまくいきません。正確には、jwt.ioに記載されているサンプルのトークンでは意図したとおりに動くのですが、実際に確認したいデータでは以下のようなExceptionになってしまいます。
Traceback (most recent call last):
.. snip ..
File "/usr/lib/python3.10/base64.py", line 87, in b64decode
return binascii.a2b_base64(s)
binascii.Error: Incorrect padding
有名な問題のようで、"binascii.Error: Incorrect padding"で検索すると「Base64の文字列を4で割り切れるように末尾に "=" を追加する(paddingする)と良い」という情報が多数見つかったため試したものの、同じエラーになってしまいました。なかなか解決できずにいたのですが、StackOverFlowのHow to decode base64 url in python?にあった下記内容で解決できました。
Alternative to @dae.eklen's solution, you can append
===
to it:s = 'iEPX-SQWIR3p67lj_0zigSWTKHg' base64.urlsafe_b64decode(s + '===')
This works because Python only complains about missing padding, but not extra padding.
結果、現在は以下のようになっています。
data = jwt_text.split(".")
decoded_header = json.loads(base64.urlsafe_b64decode(data[0] + "==="))
decoded_payload = json.loads(base64.urlsafe_b64decode(data[1] + "==="))
header = json.dumps(decoded_header, ensure_ascii=False, indent=2)
payload = json.dumps(decoded_payload, ensure_ascii=False , indent=2)
`