はじめに
boto3を使ったPythonアプリケーションで、aws loginコマンドで認証したのに、15分くらいで「クレデンシャルが期限切れです」というエラーが出て困っていました。
調べてみると、awscrtライブラリのバージョンが古いことが原因で、クレデンシャルの自動更新が失敗していたことが判明しました。
この記事では、問題の原因と解決方法を共有します。同じ問題で困っている方の参考になれば幸いです!
aws loginとは?
aws loginは、AWS CLI 2.15.0以降で追加された比較的新しいコマンドです。
従来の認証方法との違い
従来の方法(aws sso login):
aws sso login --profile my-profile
- AWS IAM Identity Center(旧AWS SSO)を使用
- 組織のSSOポータルにアクセスして認証
新しい方法(aws login):
aws login
- AWS Management Consoleの認証情報を直接使用
- ブラウザでAWSコンソールにログインするのと同じ感覚
- より直感的で簡単
aws loginの特徴
- 🔐 リフレッシュトークン対応: 一度ログインすれば、自動的にクレデンシャルが更新される(はず)
- 🚀 シンプル: プロファイル設定が不要
- 🔄 boto3と連携: Pythonアプリケーションから透過的に使える
発生した問題
症状
boto3を使ったPythonアプリケーションで、以下のような問題が発生していました:
import boto3
session = boto3.Session()
sts = session.client('sts')
# 最初は成功
identity = sts.get_caller_identity()
print(f"Account: {identity['Account']}") # OK
# 15分後...
identity = sts.get_caller_identity() # ❌ エラー!
エラーメッセージ:
ExpiredTokenException: The security token included in the request is expired
期待していた動作
boto3は、クレデンシャルの有効期限が近づくと、自動的にリフレッシュトークンを使って新しいクレデンシャルを取得してくれるはずでした。
しかし、実際にはリフレッシュが失敗していて、15分でクレデンシャルが期限切れになっていました。
原因の調査
1. クレデンシャルの確認
まず、boto3がどのクレデンシャルを使っているか確認しました:
import boto3
session = boto3.Session()
credentials = session.get_credentials()
print(f"プロバイダー: {credentials.method}")
# 出力: login
print(f"リフレッシュ可能: {isinstance(credentials, RefreshableCredentials)}")
# 出力: True
boto3は正しくloginプロバイダーを認識していて、リフレッシュ可能なクレデンシャルとして扱っていました。
2. キャッシュファイルの確認
aws loginのクレデンシャルは、以下の場所にキャッシュされています:
~/.aws/login/cache/<hash>.json
中身を見ると、リフレッシュトークンも正しく保存されていました:
{
"accessToken": {
"accessKeyId": "ASIA************",
"secretAccessKey": "**********************",
"sessionToken": "**********************",
"expiresAt": "2026-01-05T07:12:30Z"
},
"refreshToken": "**********************",
"dpopKey": "-----BEGIN EC PRIVATE KEY-----\n..."
}
3. リフレッシュの動作確認
有効期限を強制的に短くして、リフレッシュが実行されるか確認しました:
import boto3
import json
from datetime import datetime, timedelta, timezone
# キャッシュファイルを読み込み
cache_file = "~/.aws/login/cache/<hash>.json"
with open(cache_file, 'r') as f:
token_data = json.load(f)
# 有効期限を1分後に設定
new_expiry = datetime.now(timezone.utc) + timedelta(minutes=1)
token_data['accessToken']['expiresAt'] = new_expiry.strftime('%Y-%m-%dT%H:%M:%SZ')
# キャッシュファイルを更新
with open(cache_file, 'w') as f:
json.dump(token_data, f)
# boto3で新しいセッションを作成
session = boto3.Session()
sts = session.client('sts')
# STS呼び出し(リフレッシュがトリガーされるはず)
identity = sts.get_caller_identity()
すると、以下のエラーが発生しました:
AttributeError: type object 'EC' has no attribute 'decode_der_signature_to_padded_pair'
これが原因でした!
4. 根本原因の特定
boto3のソースコードを確認すると、aws loginのリフレッシュ処理では、DPoP(Demonstrating Proof-of-Possession)ヘッダーを生成する必要があることがわかりました。
このヘッダー生成に、awscrtライブラリのECクラスが使われているのですが、古いバージョン(0.28.4)では必要なメソッドが存在しないことが判明しました。
# botocore/credentials.py の該当部分(簡略化)
from awscrt.auth import EC
def _build_dpop_header(private_key, uri):
# ...
signature_bytes = EC.decode_der_signature_to_padded_pair(
signature_der, EC.CurveType.SECP384R1
) # ← このメソッドが awscrt 0.28.4 には存在しない!
解決方法
ライブラリのアップグレード
解決方法は簡単で、awscrtライブラリをアップグレードするだけでした:
pip install --upgrade awscrt botocore boto3
アップグレード後のバージョン
awscrt: 0.28.4 → 0.30.0
botocore: 1.42.19 → 1.42.21
boto3: 1.42.19 → 1.42.21
requirements.txtへの追加
今後同じ問題が起きないように、requirements.txtに最小バージョンを指定しました:
# AWS credentials refresh support
# awscrt >= 0.30.0 is required for aws login credential auto-refresh
awscrt>=0.30.0
boto3>=1.42.21
botocore>=1.42.21
確認結果
確認スクリプト
アップグレード後、リフレッシュが正常に動作するか確認しました:
# test_credential_refresh.py
import boto3
import json
from datetime import datetime, timedelta, timezone
print("=" * 80)
print("クレデンシャル自動更新テスト")
print("=" * 80)
# キャッシュファイルのパス
cache_file = "~/.aws/login/cache/<hash>.json"
# 有効期限を1分後に設定(リフレッシュを強制)
with open(cache_file, 'r') as f:
token_data = json.load(f)
original_access_key = token_data['accessToken']['accessKeyId']
print(f"元のアクセスキー: {original_access_key[:15]}...")
new_expiry = datetime.now(timezone.utc) + timedelta(minutes=1)
token_data['accessToken']['expiresAt'] = new_expiry.strftime('%Y-%m-%dT%H:%M:%SZ')
with open(cache_file, 'w') as f:
json.dump(token_data, f)
# boto3セッションを作成
session = boto3.Session()
sts = session.client('sts')
# STS呼び出し(リフレッシュがトリガーされる)
print("\nSTS呼び出しを実行...")
identity = sts.get_caller_identity()
print(f"✓ 成功: {identity['Account']}")
# リフレッシュ後のキャッシュファイルを確認
with open(cache_file, 'r') as f:
refreshed_token = json.load(f)
refreshed_access_key = refreshed_token['accessToken']['accessKeyId']
print(f"\nリフレッシュ後のアクセスキー: {refreshed_access_key[:15]}...")
if refreshed_access_key != original_access_key:
print("\n✓✓✓ 新しいクレデンシャルが取得されました!")
print("リフレッシュが正常に動作しています!")
else:
print("\n✗ クレデンシャルは更新されませんでした")
print("=" * 80)
実行結果
================================================================================
クレデンシャル自動更新テスト
================================================================================
元のアクセスキー: ASIATBMRO57RNGQ...
STS呼び出しを実行...
✓ 成功: **********
リフレッシュ後のアクセスキー: ASIATBMRO57RJ4I...
✓✓✓ 新しいクレデンシャルが取得されました!
リフレッシュが正常に動作しています!
================================================================================
完璧です! 新しいアクセスキーが取得され、リフレッシュが正常に動作していることが確認できました。
boto3のリフレッシュタイミング
boto3は、以下のタイミングでクレデンシャルのリフレッシュを試みます:
| タイミング | 説明 | 失敗時の動作 |
|---|---|---|
| Advisory Refresh | 有効期限の15分前 | 既存のクレデンシャルを使い続ける |
| Mandatory Refresh | 有効期限の10分前 | エラーを返す |
| aws login特有 | 有効期限の5分前 | エラーを返す |
つまり、有効期限の5分前から自動的にリフレッシュされるため、15分で切れる問題は解消されました。
まとめ
問題の原因
aws loginのクレデンシャルが15分で切れる原因は、awscrtライブラリのバージョンが古かったことでした。
- awscrt 0.28.4では、DPoPヘッダー生成に必要なメソッドが存在しない
- そのため、リフレッシュ時にエラーが発生していた
- boto3は自動更新に失敗し、クレデンシャルが期限切れになっていた
解決方法
pip install --upgrade awscrt botocore boto3
たったこれだけで、クレデンシャルの自動更新が正常に動作するようになりました!
ポイント
- ✅ awscrt >= 0.30.0 が必須
- ✅ boto3/botocore も最新版を推奨
- ✅ クレデンシャルは有効期限の5分前から自動更新される
- ✅ リフレッシュトークンが有効な限り、再ログイン不要
参考情報
おわりに
aws loginは比較的新しい機能なのでまだ情報が少なく、Kiroに確認してもなぜかaws sso loginを進められたり、credentialsとconfigファイルの修正が繰り返されていました・・・
この記事が、同じ問題に遭遇した方の助けになれば嬉しいです!