2025年4月にサポートが発表されたCognitoリフレッシュトークンローテーション機能を検証し、その動作とセキュリティ効果を解説します。
対象読者
- 認証機能を扱うフロントエンド・バックエンド開発者
- Cognitoを利用中または導入検討中の技術者
- セキュリティ向上に関心のあるエンジニア
背景
2025年4月、Amazon Cognitoでリフレッシュトークンローテーション機能のサポートが発表されました。
Amazon Cognito が更新トークンのローテーションに対応
この機能により、リフレッシュトークンの使い回しによるセキュリティリスクを軽減できるようになりました。
本記事では実際の検証結果を紹介します。
Cognitoが発行するトークン
| トークン | 用途 | 有効期限 | リスク |
|---|---|---|---|
| Access Token | API認可 | 5分〜1日 | 低 |
| ID Token | ID情報 | 5分〜1日 | 低 |
| Refresh Token | トークン更新 | 60分〜10年 | 高 |
リフレッシュトークンは長寿命のため、漏洩時のセキュリティリスクが高くなります。
ローテーション機能の概要
従来の動作

ローテーション有効時

効果: 使用済みトークンの無効化により、リプレイ攻撃等のリスクを軽減
検証① - 機能確認
環境設定
検証を容易にするため、以下の設定でCognitoクライアントを作成しました:
- クライアントシークレット: なし(検証の簡素化)
- トークン有効期限: 設定可能な最短値
- リフレッシュトークンローテーション: 有効
- 猶予期間: 0秒
ALLOW_REFRESH_TOKEN_AUTH: 無効(ローテーション有効時の必須設定)
注意: 本検証では猶予期間を0秒に設定していますが、実際のプロダクション環境では、ネットワーク遅延や並列リクエストを考慮して適切な猶予期間を設定する必要がありそうです。

検証コード
import boto3 import requests import time # 以下の定数はご自身の環境に合わせて編集してください REGION = "<YOUR_AWS_REGION>" USER_POOL_ID = "<YOUR_USER_POOL_ID>" COGNITO_DOMAIN = "<YOUR_COGNITO_DOMAIN>" CLIENT_ID = "<YOUR_CLIENT_ID>" USERNAME = "<YOUR_USERNAME>" PASSWORD = "<YOUR_PASSWORD>" def login(): print("Logging in via AdminInitiateAuth...") client = boto3.client("cognito-idp", region_name=REGION) resp = client.admin_initiate_auth( UserPoolId=USER_POOL_ID, ClientId=CLIENT_ID, AuthFlow="ADMIN_NO_SRP_AUTH", AuthParameters={ "USERNAME": USERNAME, "PASSWORD": PASSWORD } ) return resp["AuthenticationResult"] def refresh_token(refresh_token): print("Refreshing token via /oauth2/token...") url = f"https://{COGNITO_DOMAIN}/oauth2/token" data = { "grant_type": "refresh_token", "client_id": CLIENT_ID, "refresh_token": refresh_token, } headers = {"Content-Type": "application/x-www-form-urlencoded"} res = requests.post(url, data=data, headers=headers) res.raise_for_status() return res.json() if __name__ == "__main__": first = login() print("\nInitial login:") print("Access Token:", first['AccessToken'][:40], "...") print("Refresh Token:", first['RefreshToken'][:40], "...") time.sleep(5) # Wait a bit before refreshing second = refresh_token(first["RefreshToken"]) print("\nAfter refresh:") print("New Access Token:", second["access_token"][:40], "...") print("New Refresh Token:", second["refresh_token"][:40], "...") # Test reusing old token try: print("\nTesting old refresh token...") refresh_token(first["RefreshToken"]) print("WARNING: Old token reuse succeeded - rotation may not be enabled") except requests.exceptions.HTTPError as e: print("SUCCESS: Old token is invalidated - rotation is enabled!") print("Error:", e.response.status_code, e.response.text)
実行結果
Logging in via AdminInitiateAuth...
Initial login:
Access Token: eyJraWQiOiJBcGl1T1p2QXczeVNuNmNcL3RmTUlS ...
Refresh Token: eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwi ...
Refreshing token via /oauth2/token...
After refresh:
New Access Token: eyJraWQiOiJBcGl1T1p2QXczeVNuNmNcL3RmTUlS ...
New Refresh Token: eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwi ...
Testing old refresh token...
Refreshing token via /oauth2/token...
SUCCESS: Old token is invalidated - rotation is enabled!
Error: 400 {"error":"invalid_grant"}
結果: 古いリフレッシュトークンが正常に無効化されることを確認。
検証② - 有効期限への影響
疑問点
AWSの発表では「中断なくアクセス維持」とありますが、これは有効期限延長を意味するのでしょうか?
デベロッパーガイドには「新しい更新トークンは、元の更新トークンの残りの期間有効」と記載されています。
検証方法
1分間隔でトークンを更新し続け、元の有効期限で停止するかを確認します。
import boto3 import requests import time from datetime import datetime # 追加 # 定数定義(REGION、USER_POOL_ID等)とlogin()、refresh_token()関数は # test00.pyと同じ内容のため省略 if __name__ == "__main__": result = login() refresh = result["RefreshToken"] access = result["AccessToken"] print("\nStart time:", datetime.now()) print("Initial refresh token (first 30 chars):", refresh[:30], "...") while True: try: print(f"[{datetime.now().strftime('%H:%M:%S')}] Updating token...") result = refresh_token(refresh) access = result.get("access_token") new_refresh = result.get("refresh_token") if new_refresh: refresh = new_refresh # Update if rotated print("Refresh token rotated.") else: print("Refresh token not rotated (same).") except requests.exceptions.HTTPError as e: print(f"Update failed at {datetime.now()}") print("Error:", e.response.status_code, e.response.text) break time.sleep(60) # Wait 1 minute
結果
※長いので途中のログは省略しています。
Logging in via AdminInitiateAuth...
Start time: 2025-07-12 14:15:15.749232
Initial refresh token (first 30 chars): eyJjdHkiOiJKV1QiLCJlbmMiOiJBMj ...
[14:15:15] Updating token...
Refresh token rotated.
[14:16:16] Updating token...
Refresh token rotated.
[14:17:17] Updating token...
Refresh token rotated.
... (1分おきの継続的なローテーション) ...
[15:14:11] Updating token...
Refresh token rotated.
[15:15:12] Updating token...
Refresh token rotated.
[15:16:13] Updating token...
Update failed at 2025-07-12 15:16:14.626224
Error: 400 {"error":"invalid_grant"}
結果: 初回発行から約61分後に停止しています。有効期限は延長されないことがわかりました。
結論
ローテーション機能は...
まとめ
検証結果
2つの検証により、以下のことが確認できました:
ローテーション機能は正常に動作
- 古いリフレッシュトークンは無効化される
{"error":"invalid_grant"}でエラーが返される
有効期限は延長されない
- 初回発行から設定した期限(今回は60分)で無効化
- ローテーションしても元のトークンの残り期間を継承
AWSの発表の「中断なくアクセス維持」の意味
- 有効期限延長ではなく、セキュアなローテーションによる安全性向上
- 期限内であれば、毎回新しいトークンで安全に更新可能
実装時の注意点
今回の検証から分かった実装時の注意点:
- エラーハンドリングが必須: 古いトークンの再利用は400エラーとなる
- 設定の確認:
ALLOW_REFRESH_TOKEN_AUTHを無効にする必要がある - 猶予期間の設定: 0秒の場合は即座に無効化、最大60秒まで設定可能
- 実際のプロダクション環境では、ネットワーク遅延や並列リクエストを考慮して、適切な猶予期間を設定する必要があると考えられます。
リフレッシュトークンローテーションは、認証セキュリティを向上させる有効な機能であることがわかりました。