とある静的なWebサイトからCognitoを使って認証したいとき、ありますよね。

でもCognito君はとても親切なので、AWSコンソールからユーザをつくると
(たぶん)内部的にはAdminCreateUserっちゅーAPIが叩かれます。

そうして作成されたユーザのステータスは FORCE_CHANGE_PASSWORD 表記になり、
一定期間経過すると利用できなくなってしまいます。

この仕様がネックになってCognito君の親切心に心のなかで謝罪しながら認証を通したい場合がでてくるかもしれません。僕はでてきました。
というわけでこんな感じでやりました。

Login.js

export const getUserPool = () => {
    const poolData = {
        UserPoolId: "<EnterYourUserPoolID>",
        ClientId: "<EnterYourUserPoolClientId>",
    };

    return new Cognito.CognitoUserPool(poolData);
};

export const login = params => {
    const authenticationData = {
        Username: params.username,
        Password: params.password,
    };

    const authenticationDetails = new Cognito.AuthenticationDetails(authenticationData);
    const userPool = getUserPool();
    const cognitoUser = new Cognito.CognitoUser({
        Username: params.username,
        Pool: userPool
    });

    return new Promise((resolve, reject) => {
        const authConfig = {
            onSuccess(result) {
                const user = userPool.getCurrentUser();

                resolve({
                    code: 200,
                    messsage: 'ログインに成功しました。',
                    user: {
                        name: user.getUsername(),
                    }
                });
            },
            onFailure(err) {
                reject({
                    code: 400,
                    message: err
                });
            },
            newPasswordRequired(userAttributes, requiredAttributes) {
                console.log("newPasswordRequired()");
                console.log(userAttributes, requiredAttributes);
                cognitoUser.completeNewPasswordChallenge(params.password, {}, authConfig);
            },
            mfaRequired(challengeName, challengeParameters) {
                console.log("mfaRequired()");
                console.log(challengeName, challengeParameters);
            },
            customChallenge(challengeParameters) {
                console.log("customChallenge()");
                console.log(challengeParameters);
            }
        };

        cognitoUser.authenticateUser(authenticationDetails, authConfig);
    });
};

解説

解説していきます。

仮パスワードユーザがログインしたとき(FORCE_CHANGE_PASSWORDになってる人とか)は、 newPasswordRequired() が呼ばれます。
ここで新しいパスワードを入力することでステータスが CONFIRMED となってハッピーめでたしめでたしになるっていうのが正規のシナリオです。

でも今回は謝罪したいので、 ユーザがログインしようとして渡されたパスワードを入力したら勝手に新パスワード入力扱いになって無事ログインできちゃう みたいな振る舞いを実装します。
まずはCallbackで呼ばれる関数を authConfig としてまとめておきます。
これは、 authenticateUser() 時に呼ばれることを想定したCallbackですが、再利用性を高めるためにまとめてます。

次に、無事に authenticateUser() が叩かれ、 newPasswordRequired() Callbackが呼ばれます。
ここが大事なところで、入力されたパスワード(Cognito的には検証済み)を渡すことで、
新しいパスワード入力フェーズ をパスすることができます。

本当にCognito君ごめんなさい。

使い方

import {login} from "./Login"

login
  .then(user => {
    // ログイン成功後処理
  })
  .catch(err => {
    // エラー処理
  });

元記事はこちら

Cognitoに心のなかで謝罪しながら認証を通す