CognitoUserPoolAuthorizer認証で必要になるIDトークン
以下の記事はアメブロで2021-09-26に投稿したものです。
aws上にREST-APIをpythonで実装する場合、
フレームワークにchalice、認証にCognitoUserPoolAuthorizerとなることも多いと思う。
CognitoUserPoolAuthorizer認証だと、API入り口では、chaliceドキュメントにある通り
from chalice import CognitoUserPoolAuthorizer
authorizer = CognitoUserPoolAuthorizer(
'MyPool', provider_arns=['arn:aws:cognito:...:userpool/name'])
@app.route('/user-pools', methods=['GET'], authorizer=authorizer)
def authenticated():
return {"success": True}
というコードになるが、
このAPIをテストしようとcurlやchalice.testでリクエストを投げる際に
IDトークンをAuthorizationリクエストヘッダに設定する必要があるのだ。
このIDトークンを得る方法について、手軽なサンプルがなかったので、おぼえ書としても残しておきたい。
まぁ、こいつを取得するのに面倒な気がしたので、以下に手順を記す。
参考URL
https://qiita.com/jp_ibis/items/4fffb3c924504f0ce6fb
https://qiita.com/yakult/items/2cbb2f57c97487b6268b
1.aws_access_key_id、aws_secret_access_keyに紐づいたIAMにcognito関連の権限をつける
(AmazonCognitoPowerUserポリシーとかAdministratorAccessポリシーでOK)
2.AWSコンソールのCognitoでUserPoolを作成する。
(名称は適当、すべてデフォルトで作成、パスワード強度は落とした方が楽)
3.「全般設定」----->「アプリクライアント」----->「アプリクライアントの追加」
名称は適当でよい
「クライアントシークレットを生成」------->チェックをはずす、(参考サイトだと)という説明になっているがコード例のようにSECRET_HASHを設定すれば良いだけなので、どちらでもよい
「ALLOW_USER_PASSWORD_AUTH」------->チェックをつける
4.「全般設定」----->「ユーザーとグループ」----->「ユーザーの作成」
ユーザー名は適当でよい
「仮パスワード」------->何か入力する
「電話番号を検証済みにしますか?」------->チェックをはずす
「Eメール」------->何か入力する
ちなみにユーザーの削除は、無効化すると削除ボタンが出てきて可能になる
4.環境変数にACCESS_KEYとSECRET_ACCESS_KEYを設定して以下のコードを実行
このコードはpython、chalice、boto3、pytestがインストールされてる環境からpytest -v --capture=noで実行する
import os
import boto3
import sys
import hmac, hashlib, base64
clientId = アプリクライアントID
username = 作成したユーザー名
password = 設定した仮パスワード
app_client_secretkey = アプリクライアントのシークレット
def new_password_required(aws_client, challengeName, session):
try:
secret_hash = create_secret_hash()
aws_result = aws_client.respond_to_auth_challenge(
ClientId = clientId,
ChallengeName = challengeName,
Session = session,
ChallengeResponses={
'USERNAME': username,
'NEW_PASSWORD': password,
'SECRET_HASH': secret_hash
}
)
return aws_result
except Exception as e:
print('')
print('###############################')
print('cognito respond_to_auth_challenge exception')
print(e)
print('###############################')
return None
def create_secret_hash():
message = bytes(username + clientId, 'utf-8')
return base64.b64encode(hmac.new(bytes(app_client_secretkey,'utf-8'), message, digestmod=hashlib.sha256).digest()).decode()
def cognito_auth(aws_client):
try:
secret_hash = create_secret_hash()
aws_result = aws_client.initiate_auth(
ClientId = clientId,
AuthFlow = "USER_PASSWORD_AUTH",
AuthParameters = {
"USERNAME": username,
"PASSWORD": password,
'SECRET_HASH': secret_hash
}
)
return aws_result
except Exception as e:
print('')
print('###############################')
print('cognito auth exception')
print(e)
print('###############################')
return None
def test_get_id_token():
aws_client = boto3.client('cognito-idp',
region_name = "ap-northeast-1",
aws_access_key_id = os.environ["ACCESS_KEY"],
aws_secret_access_key = os.environ["SECRET_ACCESS_KEY"]
)
aws_result = cognito_auth(aws_client)
if 'ChallengeName' in aws_result.keys() and aws_result['ChallengeName'] == 'NEW_PASSWORD_REQUIRED':
# ユーザーを作成した直後はパスワード変更を要求されるので、このコードが走る
aws_result = new_password_required(aws_client, aws_result['ChallengeName'], aws_result['Session'])
if aws_result is not None:
aws_result = cognito_auth(aws_client)
if aws_result is None:
assert False
return
print('')
print('###############################')
for key in aws_result.keys():
print(f'{key} = {aws_result[key]}')
print('###############################')
print(aws_result["AuthenticationResult"]["IdToken"])
print('###############################')
5.IDトークンとして使う
aws_result["AuthenticationResult"]["IdToken"]
なお、上記コードでは本パスワードも仮パスワードと同じになる。
コメント
コメントを投稿