はじめに
Cognitoでログイン画面を実装し、そのログイン情報をlambdaなどで処理する際にJWT(Json Web Token)をデコードして使用するだろう。
基本的にはBase64+jsonでデコード可能なのだが、一部ハマるポイントがある。
事象
処理できる文字列と処理できない文字列が存在した。
問題が起こったコードとその挙動
import base64
# JWTを"."で分割して、ヘッダ、ペイロード、署名に分ける
header, payload, signature = jwt_string.split(".")
# ヘッダとペイロードをデコード
decoded_header = base64.b64decode(header).decode("utf-8")
decoded_payload = base64.b64decode(payload).decode("utf-8")
# ヘッダ、ペイロード、署名を表示
print("Header: ", decoded_header)
print("Payload: ", decoded_payload)
print("Signature: ", signature)
(例)ログイン情報
ユーザID:test_111
→(JWT)eyJvcmlnaW5fanRpIjoiOG5fanRpIjoiOG5fanRpIjoiOG~~~
→(デコード)ユーザID:test_111
ユーザID:test_AAA
→(JWT)eyJvcmlnaW5fanRpIjoiOG5fanRpIjoiOG5fanRpIjoiOG~~~
→(デコード)エラー:Error: Incorrect padding
通常のBase64エンコードでは対応できない
早速調べたら、下記のような記事を発見した。4で割れるように文字列を=
で調整しなさいということだ。
base64_str += "=" * ((4 - len(base64_str) % 4) % 4)
原因
根本的な原因はJETがURL-safe
という仕様だから。
通常のBase64エンコードでは+
、/
を意識せずに変換しますが、それらの記号はURL上では特別な意味をもってしまうため、それも別な文字に置き換える必要がある。
+ を - に置換
/ を _ に置換
解決策
Base64 URL Encode
を使用するのが無難であろう。ただ、確実性と可読性からpython-jose
ライブラリを使用して問題解決した。
レイヤーを追加する手間はあるが、改ざんも発見可能で日本語の難しい漢字も確実にデコードしてくれるのでこちらを使用する。
レイヤーを追加する場合のコマンド
$ mkdir python
$ pip3 install -t python python-jose
$ zip -r9 layer.zip python
参考:https://sebenkyo.com/2021/05/21/post-1979/
修正したコード
jwt_str = response['AuthenticationResult']['IdToken']
# python-joseでidtokenをdecode
payload = jwt.get_unverified_claims(jwt_str)
# 結果をコンソールに表示
print(payload)