Cognito ユーザープールの Lambda トリガーを使ってサインイン条件をカスタマイズしてみた

2023.03.27

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

いわさです。

Cognito ユーザープールではサインアップやサインインの標準フローが用意されているのですが、Lambda トリガー機能を使うことでカスタムすることが出来ます。
今回サインイン時に「カスタム属性の設定値が条件を満たす」という条件判定を追加したかったので Lambda トリガーを使ってみました。

サインインの判定に利用できる Lambda トリガーは上記の「認証前の Lambda トリガー」となり、次のように標準サインイン判定の前処理として実施されるものとなっています。

今回は標準のサインインに加えて、特定のカスタム属性に設定したフラグが ON の場合のみサインインできるように変更してみたいと思います。

ユーザープール設定

ユーザープールの設定ですが、今回はサインインオプションに E メールを選択しています。

今回はクライアントからのセルフサインアップフローを想定しており、その過程で E メール確認済みかどうかもテストパターンとして考慮しておきたいと思います。
そのため、「セルフサービスのサインアップ」は有効化しています。

同様にセルフサインアップの E メール検証前後での挙動を確認したいので検証コードを自動送信するようにしています。

今回はサインインの追加条件としてカスタム属性を参照したいので適当なカスタム属性を追加しています。

ホストされた UI を使わずに initiate-auth でサインインさせます。

Lambda トリガーを設定

Lambda トリガーを設定するために Lambda 関数を用意しておきます。
次のようにカスタム属性のフラグを判定して例外を発生させるだけの関数です。

認証前 Lambda トリガーではエラーを発生させることでサインイン結果をエラーとすることが出来ます。

lambda_function.py

import json

def lambda_handler(event, context):
    print(event)
    if event['request']['userAttributes']['custom:hoge1'] == '0' or event['request']['userAttributes']['custom:hoge2'] == '0':
        raise Exception("hoge cannot authenticate.")
    return event

Lambda 関数を作成したら、Cognito ユーザープールの設定画面から 認証前 Lambda トリガーとして作成した Lambda 関数を指定します。

ユーザー作成

順不同でちょっと見づらく恐縮ですが、以下の 5 件のユーザーを作成します。

ユーザー E メール確認 フラグ1 フラグ2
ユーザー1 確認済み ON ON
ユーザー2 確認済み ON OFF
ユーザー3 確認済み OFF OFF
ユーザー4 未確認 OFF OFF
ユーザー5 未確認 ON ON

ユーザー1だけが作成出来てほしいですね。

1. ユーザー作成

sign-up コマンドを使って、セルフサインアップを行います。

% cat user1.json
{
    "ClientId": "5bdnp3cl147tbn9tps0rqpbt26",
    "Username": "iwasa.takahito+user1@example.com",
    "Password": "hogehoge",
    "UserAttributes": [
        {
            "Name": "custom:hoge1",
            "Value": "0"
        },
        {
            "Name": "custom:hoge2",
            "Value": "0"
        }
    ]
}
% aws cognito-idp sign-up --cli-input-json file://user1.json
{
    "UserConfirmed": false,
    "CodeDeliveryDetails": {
        "Destination": "i***@c***",
        "DeliveryMedium": "EMAIL",
        "AttributeName": "email"
    },
    "UserSub": "8c962c9c-61f9-424b-8f37-63fa6cc4c5d3"
}

この時点ではまだユーザー4とユーザー5の「未確認」状態です。
この時点では Lambda トリガー関係なしにサインインがまだ出来ない状態です。

サインアップ処理後、登録したメールアドレスへ検証コードが送信されます。

Your confirmation code is 597706

2. 検証コードを送信

検証コードを受け取ったユーザーは confirm-sign-up を使って E メール検証を行います。

% cat user2.json
{
    "ClientId": "5bdnp3cl147tbn9tps0rqpbt26",
    "Username": "iwasa.takahito+user1@example.com",
    "ConfirmationCode": "597706"
}
% aws cognito-idp confirm-sign-up --cli-input-json file://user2.json

この時点で確認済みステータスに変更されました。ユーザー3の状態です。
通常のサインイン処理であれば、この時点でサインインができるようになります。が、カスタム属性のフラグはまだ更新されていないので Lambda トリガーによってサインインが拒否されます。

3. カスタム属性フラグを設定

属性更新は管理コマンド(admin-update-user-attributes)を使用します。
実際には信頼されるサーバーサイド API に権限を付与して実行する形になると思います。

ここでは 2 つのフラグが Lambda トリガーの条件になっているので、1 つのフラグのみ ON の場合と 2 つとも ON になっている場合を用意します。(ユーザー1とユーザー2)

% cat user4.json                                                               
{
    "UserPoolId": "ap-northeast-1_SjpNJWhzZ",
    "Username": "iwasa.takahito+user1@example.com",
    "UserAttributes": [
        {
            "Name": "custom:hoge1",
            "Value": "1"
        }
    ]
}
% aws cognito-idp admin-update-user-attributes --cli-input-json file://user4.json

サインイン確認

ではサインインしてみましょう。
サインインには initiate-auth コマンドを使います。

ユーザー1:サインイン出来る

% aws cognito-idp initiate-auth --cli-input-json  file://signin1.json            
{
    "ChallengeParameters": {},
    "AuthenticationResult": {
        "AccessToken": "...",
        "ExpiresIn": 3600,
        "TokenType": "Bearer",
        "RefreshToken": "...",
        "IdToken": "..."
    }
}

ユーザー2:サインイン出来ない

認証前 Lambda でエラーUserLambdaValidationExceptionが発生しました。

% aws cognito-idp initiate-auth --cli-input-json  file://signin2.json

An error occurred (UserLambdaValidationException) when calling the InitiateAuth operation: PreAuthentication failed with error hoge cannot authenticate..

ユーザー3:サインイン出来ない

% aws cognito-idp initiate-auth --cli-input-json  file://signin3.json

An error occurred (UserLambdaValidationException) when calling the InitiateAuth operation: PreAuthentication failed with error hoge cannot authenticate..

ユーザー4:サインイン出来ない

認証前の Lambda でエラーが発生しているので、未確認だからエラーというわけではなく、カスタム属性のフラグが条件を満たしていないために標準サインイン処理まで実行されていないよ、という形ですね。

% aws cognito-idp initiate-auth --cli-input-json  file://signin4.json

An error occurred (UserLambdaValidationException) when calling the InitiateAuth operation: PreAuthentication failed with error hoge cannot authenticate..

ユーザー5:サインイン出来ない

Lambda トリガーによる認証前処理のエラーメッセージは表示されていませんが、標準のサインイン処理で未確認だからダメだよと拒否されていますね。
UserNotConfirmedExceptionが発生しています。

% aws cognito-idp initiate-auth --cli-input-json  file://signin5.json

An error occurred (UserNotConfirmedException) when calling the InitiateAuth operation: User is not confirmed.

さいごに

本日は Cognito ユーザープールの Lambda トリガーを使ってサインイン条件をカスタマイズしてみました。

サインイン処理を拡張することが出来ましたが、「認証前」処理という点に注意が必要ですね。
また、サインイン API はフロントエンドから直接エンドポイントへアクセスすると思うので、フロントエンド側でカスタム処理によるエラーの場合どのような表示になるのかも確認が必要です。