認証フローの切り替え
クライアント側の認証には、3つの異なるフローがあります。
-
USER_SRP_AUTH:USER_SRP_AUTHフローは、SRPプロトコル(Secure Remote Password)を使用します。このプロトコルではパスワードがクライアントから出ることはなく、サーバーに知られることもありません。これは推奨されるフローであり、デフォルトで使用されます。 -
USER_PASSWORD_AUTH:USER_PASSWORD_AUTHフローは、ユーザーの認証情報を暗号化されていない状態でバックエンドに送信します。Cognito への「Migration」トリガーを使用してユーザーを移行し、ユーザーにパスワードのリセットを強制しないようにしたい場合は、この認証タイプを使用する必要があります。これは、トリガーによって起動されるLambda関数が提供された認証情報を検証する必要があるためです。 -
CUSTOM_AUTH:CUSTOM_AUTHフローは、異なる要件に対応するようにカスタマイズできるチャレンジとレスポンスのサイクルを許可するために使用されます。
異なるフローを使用するようにAuthを設定するには、以下を実行します。
Auth.configure({ // other configurations... // ... authenticationFlowType: 'USER_SRP_AUTH' | 'USER_PASSWORD_AUTH' | 'CUSTOM_AUTH'});認証フローの詳細については、AWS Cognito開発者ドキュメントをご覧ください。
USER_PASSWORD_AUTH フロー
USER_PASSWORD_AUTH認証フローの使用例は、ユーザーをAmazon Cognitoに移行する場合です。
認証バックエンドの設定
USER_PASSWORD_AUTH認証フローを使用するには、Cognito アプリクライアントがこれを許可するように設定されている必要があります。AWS コンソールでは、General settings > App clients > Show Details(対象のクライアント用)> Enable username-password (non-SRP) flowのチェックボックスをチェックして行います。AWS CLI または CloudFormation を使用している場合は、「Explicit Auth Flows」のリストにUSER_PASSWORD_AUTHを追加してアプリクライアントを更新してください。
Amazon Cognito でユーザーを移行する
Amazon Cognito は、既存のユーザーディレクトリから Cognito にユーザーをシームレスに移行するためのトリガーを提供します。これは、ユーザープールの「Migration」トリガーを設定することで実現でき、ユーザープールに既に存在しないユーザーが認証またはパスワードをリセットするたびにLambda関数を起動します。
簡単に言うと、Lambda関数は既存のユーザーディレクトリに対してユーザーの認証情報を検証し、成功した場合はユーザー属性とステータスを含むレスポンスオブジェクトを返します。エラーが発生した場合はエラーメッセージが返されます。この移行フローの設定方法についてはこちらのドキュメント、Lambda がリクエストとレスポンスオブジェクトを処理する方法の詳細な手順についてはこちらをご覧ください。
CUSTOM_AUTH フロー
Amazon Cognito User Pools では認証フローをカスタマイズして、パスワードに加えてカスタムチャレンジタイプを有効にしてユーザーのアイデンティティを検証することをサポートしています。これらのチャレンジタイプには、CAPTCHA または動的チャレンジ質問が含まれる場合があります。
カスタム認証フロー用のチャレンジを定義するには、Amazon Cognito 用に3つのLambda トリガーを実装する必要があります。
カスタム認証フロー
アプリでカスタム認証フローを開始するには、パスワードなしでsignInを呼び出します。カスタムチャレンジはsendCustomChallengeAnswerメソッドを使用して回答する必要があります。
import { Auth } from 'aws-amplify';
Auth.configure({ // other configurations // ... authenticationFlowType: 'CUSTOM_AUTH'});
type SignInParameters = { username: string; password: string;};
export async function signIn({ username, password }: SignInParameters) { const challengeResponse = 'the answer for the challenge'; try { const user = await Auth.signIn(username, password); if (user?.challengeName === 'CUSTOM_CHALLENGE') { try { // to send the answer of the custom challenge const challengeAnswerResponse = await Auth.sendCustomChallengeAnswer( user, challengeResponse ); console.log(challengeAnswerResponse); } catch (err) { console.log(err); } } } catch (err) { console.log(err); }}import { Auth } from 'aws-amplify';
Auth.configure({ // other configurations // ... authenticationFlowType: 'CUSTOM_AUTH'});
async function signIn(username, password) { const challengeResponse = 'the answer for the challenge'; try { const user = await Auth.signIn(username, password); if (user?.challengeName === 'CUSTOM_CHALLENGE') { try { // to send the answer of the custom challenge const challengeAnswerResponse = await Auth.sendCustomChallengeAnswer( user, challengeResponse ); console.log(challengeAnswerResponse); } catch (err) { console.log(err); } } } catch (err) { console.log(err); }}CAPTCHA ベースの認証
Lambda トリガーで CAPTCHA チャレンジを作成するサンプルを以下に示します。
Create Auth Challenge Lambda Triggerはユーザーへのチャレンジとして CAPTCHA を作成します。CAPTCHA イメージの URL と予想される答えはプライベートチャレンジパラメータに追加されます。
import { Handler } from 'aws-lambda';
export const handler: Handler = async (event) => { if (!event?.request?.session || event?.request?.session?.length === 0) { event.response.publicChallengeParameters = { captchaUrl: 'url/123.jpg' }; event.response.privateChallengeParameters = { answer: '5' }; event.response.challengeMetadata = 'CAPTCHA_CHALLENGE'; } return event;};export const handler = async (event) => { if ( !Array.isArray(event?.request?.session) || event?.request?.session?.length ) { event.response.publicChallengeParameters = { captchaUrl: 'url/123.jpg' }; event.response.privateChallengeParameters = { answer: '5' }; event.response.challengeMetadata = 'CAPTCHA_CHALLENGE'; } return event;};このDefine Auth Challenge Lambda Triggerはカスタムチャレンジを定義します。
import { Handler } from 'aws-lambda';
export const handler: Handler = async (event) => { if ( !Array.isArray(event?.request?.session) || event?.request?.session?.length ) { // If you don't have a session or it is empty then send a CUSTOM_CHALLENGE event.response.challengeName = 'CUSTOM_CHALLENGE'; event.response.failAuthentication = false; event.response.issueTokens = false; } else if ( event?.request?.session?.length === 1 && event?.request?.session[0]?.challengeResult ) { // If you passed the CUSTOM_CHALLENGE then issue token event.response.failAuthentication = false; event.response.issueTokens = true; } else { // Something is wrong. Fail authentication event.response.failAuthentication = true; event.response.issueTokens = false; }
return event;};export const handler = async (event) => { if ( !Array.isArray(event?.request?.session) || event?.request?.session?.length ) { // If you don't have a session or it is empty then send a CUSTOM_CHALLENGE event.response.challengeName = 'CUSTOM_CHALLENGE'; event.response.failAuthentication = false; event.response.issueTokens = false; } else if ( event?.request?.session?.length === 1 && event?.request?.session[0]?.challengeResult ) { // If you passed the CUSTOM_CHALLENGE then issue token event.response.failAuthentication = false; event.response.issueTokens = true; } else { // Something is wrong. Fail authentication event.response.failAuthentication = true; event.response.issueTokens = false; }
return event;};Verify Auth Challenge Response Lambda Triggerはチャレンジの答えを検証するために使用されます。
import { Handler } from 'aws-lambda';
export const handler: Handler = async (event, context) => { if ( event?.request?.privateChallengeParameters?.answer === event?.request?.challengeAnswer ) { event.response.answerCorrect = true; } else { event.response.answerCorrect = false; } return event;};export const handler = async (event, context) => { if ( event?.request?.privateChallengeParameters?.answer === event?.request?.challengeAnswer ) { event.response.answerCorrect = true; } else { event.response.answerCorrect = false; } return event;};