Amazon Cognitoの認証フローは複数ありますが、サーバーサイドの処理のパターンから代表的な USER_PASSWORD_AUTH
と USER_SRP_AUTH
を行う方法を書きます。
AWSの資料から引用した以下の表の〇部分です。
引用元:https://d1.awsstatic.com/webinars/jp/pdf/services/20200630_AWS_BlackBelt_Amazon%20Cognito.pdf
準備
IAM User
curlでの実行でも不要ですが、boto3でも不要です。
CognitoユーザープールID取得
ユーザプールIDをコピーしておきます。
アプリケーションクライアント作成
Cognitoユーザプール > アプリケーション統合 > アプリケーションクライアントの作成から、アプリケーションクライアントを作成します。
ブログ用に一時的に作った以下のクライアントを使います。
シークレット有り版も作っておきます。
USER_PASSWORD_AUTH
ユーザとパスワードを送ることで認証するシンプルなパターンです。
curl
curlで実行する場合は以下のようにリクエスト送ると、
REGION=ap-northeast-1 USER=yusuke.otomo@dummy.co.jp PASSWORD=P@ssw0rd CLIENT_ID=52tr3abq5hnj7ui6mkidadedcq curl -XPOST -H 'Content-Type: application/x-amz-json-1.1' \ -H 'X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth' \ -d "{\"AuthFlow\": \"USER_PASSWORD_AUTH\", \"AuthParameters\": {\"USERNAME\": \"${USER}\", \"PASSWORD\": \"${PASSWORD}\"}, \"ClientId\": \"${CLIENT_ID}\"}" \ https://cognito-idp.$REGION.amazonaws.com
以下のようにトークン取得できます。
{ "AuthenticationResult": { "AccessToken": "eyJ...", "ExpiresIn": 3600, "IdToken": "eyJ...", "RefreshToken": "eyJ...", "TokenType": "Bearer" }, "ChallengeParameters": {} }
Python
import boto3 from pprint import pprint # IAM権限は不要です。空権限のユーザ・ロールでもOKv cognito_cli = boto3.client("cognito-idp",region_name="ap-northeast-1") client_id = "52tr3abq5hnj7ui6mkidadedcq" username = "yusuke.otomo@dummy.co.jp" password = "P@ssw0rd" res = cognito_cli.initiate_auth( AuthFlow='USER_PASSWORD_AUTH', AuthParameters={ 'USERNAME': username, 'PASSWORD': password }, ClientId=client_id, ) pprint(res["AuthenticationResult"])
トークン取得できました。
{'AccessToken': 'eyJ...', 'ExpiresIn': 3600, 'IdToken': 'eyJ...', 'RefreshToken': 'eyJ...', 'TokenType': 'Bearer'}
Python(クライアントシークレット対応)
クライアントシークレットに対応する場合は、SECRET_HASH
の計算が必要です。
import boto3 import base64,hmac,hashlib from pprint import pprint # IAM権限は不要です cognito_cli = boto3.client("cognito-idp",region_name="ap-northeast-1") client_id = "6d22r9jmslov5ko4megustei8k" client_secret = "1jpflajn8hu40meuidgi9vg4tm89n9hktmueltgibiscbeq2dvqk" username = "yusuke.otomo@dummy.co.jp" password = "P@ssw0rd" # SECRET_HASH計算 message = bytes(username+client_id,'utf-8') key = bytes(client_secret,'utf-8') secret_hash = base64.b64encode(hmac.new(key, message, digestmod=hashlib.sha256).digest()).decode() res = cognito_cli.initiate_auth( AuthFlow='USER_PASSWORD_AUTH', AuthParameters={ 'USERNAME': username, 'PASSWORD': password, "SECRET_HASH": secret_hash, }, ClientId=client_id, ) pprint(res["AuthenticationResult"])
USER_SRP_AUTH
USER_SRP_AUTH
はパスワードを直接サーバーに送らないセキュアな方式で、AWSからも推奨されています。RFCで規定されている標準的な手法です。
- RFC 2945 - The SRP Authentication and Key Exchange System
- RFC 5054 - Using the Secure Remote Password (SRP) Protocol for TLS Authentication
引用元:https://d1.awsstatic.com/webinars/jp/pdf/services/20200630_AWS_BlackBelt_Amazon%20Cognito.pdf
動きを知りたい場合はRFPを読むか、以下のサイトが日本語で書かれていてわかりやすいです。
Secure Remote Password (SRP) Protocol
実装検討時の参考
実装する時参考になるのがAmplifyのコードです。
アプリケーションクライアント設定
ALLOW_USER_SRP_AUTH
を認証フローに追加しておきます。
Python
Pythonで上記のものを実装した内容が紹介されているのが以下の記事です。上記のSRPの仕様見ながら追ってみると面白いです。
とは言え内容見るとわかる通り、実装するのはそれなりに面倒です。。。
Pythonなら以下のライブラリを利用可能です。
pipでインストールして利用します。
pip install warrant
以下のように簡単にSRPが利用できるようになります。
import boto3 from warrant.aws_srp import AWSSRP # IAM権限は不要です。空権限のユーザ・ロールでもOK cognito_cli = boto3.client("cognito-idp", region_name="ap-northeast-1") client_id = "52tr3abq5hnj7ui6mkidadedcq" username = "yusuke.otomo@dummy.com" password = "P@ssw0rd" user_pool_id = "ap-northeast-1_Txxxxx" # 保存しておいたユーザプールID aws_srp = AWSSRP( username=username, password=password, pool_id=user_pool_id, client_id=client_id, client=cognito_cli, ) tokens = aws_srp.authenticate_user() pprint(tokens["AuthenticationResult"])
トークン取得できました。
{'AccessToken': 'eyJ...', 'ExpiresIn': 3600, 'IdToken': 'eyJ...', 'RefreshToken': 'eyJ...', 'TokenType': 'Bearer'}
ちなみに2022/08/09時点ではシークレット付きのアプリケーションクライアントに対するSRPは問題が出ているようで、利用できませんでした。これですが、PRまで出ているようなので中身見て対応も可能だと思います。
参考URL
Amazon Cognito の「クライアントのシークレットのハッシュを検証できません」というエラーを解決する | AWS re:Post