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)

Back to the tool

`