Contents
AWS Cognitoカスタム認証フローの概要と目的
AWS CognitoのCUSTOM_AUTHフローは、標準的なパスワード認証を超えた柔軟な認証プロセスを実現するための仕組みです。ユーザーがメールやSNSアカウント以外に、ハードウェアトークンや内部システムの合言葉など独自の認証方法を利用する場合に有効です。Lambdaトリガーを活用することで、OTP発行・検証やチャレンジ処理をカスタマイズ可能です。
カスタム認証フローの設計意義
標準的な認証フローでは対応できないユースケースに応えるため、CUSTOM_AUTHフローは必須です。例えば「QRコードスキャンによる認証」や「内部システムの合言葉認証」など、柔軟性を求めるシーンで活用されます。Lambdaトリガーを介して認証プロセスをカットインすることで、企業独自のセキュリティ基準を満たします。
Lambdaトリガー活用の利点
Lambdaトリガーは、認証フローの各段階(Pre Sign-Up、Auth Challengeなど)に処理を挿入可能です。これにより、OTP生成やメール送信などの非同期処理を効率的に行えます。また、NotAuthorizedExceptionなどのエラーメッセージを具体的に把握しやすい点もメリットです。
CUSTOM_AUTHフローの初期設定手順
AWSコンソールでユーザープールを作成した後、CUSTOM_AUTHフローを有効にする必要があります。このステップが不完全だと、Lambdaトリガーが正しく実行されない可能性があります。
User Pool作成時のセキュリティ設定
ユーザープールの作成時に以下の点に注意してください:
- 認証フロー選択: 「CUSTOM_AUTH」を選択し、「Password authentication」を無効化します。
- メール/電話番号の有効性: OTP発行が必要な場合は、ユーザーが登録時にメールアドレスまたは携帯電話番号を入力できるように設定します。
| 設定項目 | 必須値 | 補足 |
|---|---|---|
| 認証フロー | CUSTOM_AUTH | パスワード認証は無効化 |
| メール確認 | 有効 | OTP送信に必要 |
| ユーザー属性 | 任意 | 携帯番号を含む場合のみ有効 |
注意: カスタム認証フローを使用する際は、「Password authentication」が無効でなければならない点にご注意ください。この設定ミスが原因でNotAuthorizedExceptionが発生します。
カスタムフローの有効化手順
- AWSコンソールからCognitoユーザープールを選択します。
- 「User pool details」タブを開き、「Client apps」セクションに移動します。
- 新規アプリケーションクライアントを追加し、認証フローで「CUSTOM_AUTH」を選択します。
Lambdaトリガーによるチャレンジ処理の実装
Lambdaトリガーは、Pre Sign-UpイベントハンドラやAuth Challengeイベントハンドラに設定することで、カスタム認証を実現します。
Pre Sign-Upイベントハンドラの構成
ユーザーがサインアップ時に情報を送信すると、Lambda関数が自動的にトリガーされます。この際、チャレンジタイプ(例:SMS_OTP)を指定し、次のステップに進ませます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import boto3 def lambda_handler(event, context): # ユーザー属性取得 user_attributes = event['userAttributes'] # OTPを発行する処理をここに記述 challenge_type = 'SMS_MFA' return { 'challengeName': challenge_type, 'challengeMetadata': 'Custom Challenge Metadata' } |
ChallengeGenerationロジックのコード例
チャレンジ生成には、AWS SDK for Python (boto3) を使用します。以下は、OTPを発行する処理の一例です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import boto3 import secrets def generate_otp(): # パスワード認証では使われないため、secretsモジュールでセキュアに生成 return str(secrets.randbelow(900000) + 100000) client = boto3.client('cognito-idp', region_name='ap-northeast-1') def lambda_handler(event, context): user_pool_id = 'your_user_pool_id' # CUSTOM_AUTHではパスワードは使われないため、チャレンジのみ設定 return { 'challengeName': 'SMS_MFA', 'challengeMetadata': 'OTP was sent to user\'s phone number' } |
OTP発行・検証ロジックの実装例
OTPを動的に生成し、検証処理をLambdaトリガー内で実施します。メール送信やSMS送信は、AWS SNSなどを併用します。
ワンタイムパスワードの動的生成方法
以下は、ランダムな6桁数字を生成する例です。セキュリティ対策としてsecretsモジュールを使用しています。
|
1 2 3 4 5 |
import secrets def generate_otp(): return str(secrets.randbelow(900000) + 100000) |
注意: OTPの有効期限は標準では5分間ですが、企業によってこの値をカスタマイズ可能です。
認証ステップにおけるバリデーションフロー
ユーザーがOTPを入力すると、Lambdaトリガーで検証します。Invalid OTP formatなどのエラーメッセージは、以下のように処理されます。
|
1 2 3 4 5 6 7 8 9 10 11 |
def lambda_handler(event, context): user_input = event['code'] # 保存されたOTPと比較 if user_input == stored_otp: return { 'challengeName': 'SUCCESS' } else: raise Exception('Invalid OTP format') |
NotAuthorizedExceptionの解決策とエラーメッセージの対応
カスタム認証フローでは、NotAuthorizedExceptionが頻繁に発生します。その原因と対処法を具体的に解説します。
典型的な発生原因の分析
| 原因 | 対応策 |
|---|---|
| トークン有効期限切れ | CUSTOM_AUTHではリフレッシュトークンは使われないため、認証フローを再実行する |
| ユーザーステータス不正 | 「Disabled」状態のユーザーを確認 |
| 認証フロー設定ミス | クライアントアプリでCUSTOM_AUTH選択 |
デバッグログの確認手順
- AWS CloudWatch Logsにアクセスし、Lambdaトリガー関数のログを確認します。
- トレースIDやユーザー名から特定するエラーメッセージ(例:'Invalid OTP format')を探します。
- 「Challenge not found」というメッセージが表示されれば、チャレンジタイプが正しく設定されていない可能性があります。
LambdaトリガーのAuth Challengeイベントハンドラ実装例
CUSTOM_AUTHフローでは、ユーザーがチャレンジに応答した際、Lambdaトリガー内で検証と次のステップを処理する必要があります。以下は、ChallengeResponseを含む処理の一例です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
def lambda_handler(event, context): # チャレンジタイプの確認 if event['challengeName'] == 'SMS_MFA': user_input = event['answer'] # 保存されたOTPと比較 if user_input == stored_otp: return { 'challengeResult': True, 'challengeName': 'SUCCESS' } else: raise Exception('Invalid OTP format') # その他のチャレンジタイプの処理(例: 自動承認、外部システムとの連携) |
Flaskとの統合サンプルコード
Flaskアプリケーションでカスタム認証フローを実装するには、AWS SDK(boto3)とLambdaトリガーの連携が必要です。
API Gateway経由のリクエストフロー
- ユーザーがログインフォームに情報を入力します。
- FlaskアプリケーションがCognito Identity Provider APIを呼び出します(
InitiateAuth、RespondToAuthChallenge)。 - Lambdaトリガーが処理し、OTPの発行または検証を行います。
認証成功後のセッション管理
認証に成功すると、id_tokenとaccess_tokenが返されます。Flaskアプリケーションでこれらをセッションに保存します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
from flask import Flask, request, session import boto3 app = Flask(__name__) cognito_client = boto3.client('cognito-idp', region_name='ap-northeast-1') @app.route('/login', methods=['POST']) def login(): username = request.form['username'] # Cognitoに認証リクエストを送信(PASSWORDパラメータはCUSTOM_AUTHでは使われない) response = cognito_client.initiate_auth( AuthFlow='CUSTOM_AUTH', ClientId='your_client_id', AuthParameters={ 'USERNAME': username } ) session['id_token'] = response.get('AuthenticationResult', {}).get('IdToken') return "ログイン成功" |
- ユーザーがカスタム認証フローを理解しやすくする
- Lambdaトリガーでチャレンジ処理を柔軟に実装できる
- OTPの動的生成・検証ロジックは重要
- NotAuthorizedExceptionのエラーメッセージから原因を特定できる
- Flaskなどのフレームワークと連携して開発環境を整える