AWS Cognitoの認証フローをフローチャートにしてみた

# はじめに
AWS Cognitoを使った認証で、`signInStep`の遷移が分かりづらかったのでフローチャートにしました。

:::note warn
間違っている or 情報が古い可能性があるので、参考にする際はご注意ください。
:::


# フローチャート
## ユーザー新規追加～初回ログイン

```mermaid
flowchart

INPUT[メアド/仮パスワード入力\n（必要であれば電話番号も）]
IS_CUSTOM{カスタム認証？}
CONF_CUSTOM(CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE)
CONF_PW(CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED)
MFA_TYPE{MFAは必須/任意/無効？}

WHAT_MFA_SETTING{ユーザープールで\n使えるMFAの種類は？}

CONFIRMED_PHONENUM{電話番号は\n登録済み？}

CONT_TOTP(CONTINUE_SIGN_IN_WITH_TOTP_SETUP)
CONF_SMS(CONFIRM_SIGN_IN_WITH_SMS_CODE)
DONE1(DONE)
DONE2(DONE)
DONE3(DONE)
DONE4(DONE)
TYPE_1[ユーザーのMFA設定：①]
TYPE_6_1[ユーザーのMFA設定：⑥-1]
TYPE_6_2[ユーザーのMFA設定：⑥-2]
TYPE_NONE[ユーザーのMFA設定：非アクティブ]

ユーザー登録 --> INPUT --> IS_CUSTOM
  IS_CUSTOM -->|no| CONF_PW --> 仮パスワード更新 --> MFA_TYPE
      MFA_TYPE -->|必須| WHAT_MFA_SETTING
        WHAT_MFA_SETTING -->|TOTPのみ| TYPE_6_1 --> CONT_TOTP
        WHAT_MFA_SETTING -->|SMS/TOTP両方| CONFIRMED_PHONENUM
          CONFIRMED_PHONENUM -->|no| TYPE_6_1
          CONFIRMED_PHONENUM -->|yes| TYPE_1 --> CONF_SMS
        WHAT_MFA_SETTING -->|SMSのみ| TYPE_1
      MFA_TYPE -->|任意| TYPE_NONE
      MFA_TYPE -->|無効| TYPE_NONE
  IS_CUSTOM -->|yes| CONF_CUSTOM --> カスタム認証成功 --> DONE1

CONF_SMS --> DONE2
CONT_TOTP --> TYPE_6_2 --> DONE3
TYPE_NONE --> DONE4
```
ユーザーのMFA設定には以下の5パターンがある
ユーザー追加～初回ログインの段階では、①か⑥にしかならない模様
- ①SMS のみ
- ②Authenticator アプリケーションのみ
- ③SMS (利用可能な場合)、それ以外の場合は認証アプリケーション
- ④認証アプリケーション (利用可能な場合)、それ以外の場合は SMS
- ⑤希望する配信方法の選択
→ユーザーにどちらで認証するか選択させ、選択した方法のフローに移行する
- ⑥上記いずれにも該当しない
⑥-1. TOTPトークンを登録する前
→この状態でログインしようとすると`CONTINUE_SIGN_IN_WITH_TOTP_SETUP`チャレンジが返され、TOTPトークンの登録が促される
⑥-2. TOTPトークン登録済み
→この状態でログインしようとすると`CONFIRM_SIGN_IN_WITH_TOTP_CODE`チャレンジが返され、登録したTOTPトークンが求められる

## ユーザーのMFA設定の変更
フローチャートではないが、補足情報として以下記載

- ユーザープールでSMS, TOTPどちらも利用可能
- ユーザーの電話番号が登録済み
- ユーザーがTOTPトークンを登録済み

上記をすべて満たす場合、`AdminSetUserMFAPreferenceCommand`などでユーザーのMFA設定を変更できる

```typescript
import { AdminSetUserMFAPreferenceCommand } from "@aws-sdk/client-cognito-identity-provider";

const setPref = async () => {
  const SMSMfaSettings = {
    Enabled: false,
    PreferredMfa: false,
  }
  const SoftwareTokenMfaSettings = {
    Enabled: true,
    PreferredMfa: true,
  }
  const input = {
    SMSMfaSettings,
    SoftwareTokenMfaSettings,
    Username: "<UUID>",
    UserPoolId: "<ユーザープールID>",
  };
  const command = new AdminSetUserMFAPreferenceCommand(input);
  await client.send(command);
}
```

<table>
  <thead>
    <tr> <th colspan="2"></th> <th>①</th> <th>②</th> <th>③</th> <th>④</th> <th>⑤</th> </tr>
  </thead>
  <tr> <th rowspan="2">SMSMfaSettings</th> <th>Enabled</th> <td>true</td>  <td><font color="blue">false</font></td> <td>true</td>  <td>true</td> <td>true</td>   </tr>
  <tr> <th>PreferredMfa</th> <td>true</td>  <td><font color="blue">false</font></td> <td>true</td>  <td><font color="blue">false</font></td> <td><font color="blue">false</font></td> </tr>
  <tr> <th rowspan="2">SoftwareTokenMfaSettings</th> <th>Enabled</th> <td><font color="blue">false</font></td> <td>true</td>  <td>true</td>  <td>true</td>  <td>true</td>  </tr>
  <tr> <th>PreferredMfa</th> <td><font color="blue">false</font></td> <td>true</td>  <td><font color="blue">false</font></td> <td>true</td>  <td><font color="blue">false</font></td> </tr>
</table>

`AdminSetUserMFAPreferenceCommand`APIで⑥の状態にはできなさそう
一応、②の状態がほぼ⑥-2と同じと思われる

## 登録済みユーザーのログイン
```mermaid
flowchart

IS_CUSTOM{カスタム認証？}
CONF_CUSTOM(CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE)
WHICH_CONFIG{ユーザーの\nMFA設定は？}
CONT_TOTP(CONTINUE_SIGN_IN_WITH_TOTP_SETUP)
CONF_TOTP(CONFIRM_SIGN_IN_WITH_TOTP_CODE)
CONF_SMS(CONFIRM_SIGN_IN_WITH_SMS_CODE)
CONT_SELECT(CONTINUE_SIGN_IN_WITH_MFA_SELECTION)
WITCH{どちらを選択？}
DONE1(DONE)
DONE2(DONE)
DONE3(DONE)
DONE4(DONE)

メアド/パスワード入力 --> IS_CUSTOM
  IS_CUSTOM -->|no| WHICH_CONFIG
    WHICH_CONFIG -->|②| CONF_TOTP
    WHICH_CONFIG -->|④| CONF_TOTP
    WHICH_CONFIG --->|⑥-1| CONT_TOTP --> DONE1
    WHICH_CONFIG -->|⑥-2| CONF_TOTP
    WHICH_CONFIG -->|⑤| CONT_SELECT --> WITCH
      WITCH -->|TOTP| CONF_TOTP
      WITCH -->|SMS| CONF_SMS
    WHICH_CONFIG -->|①| CONF_SMS
    WHICH_CONFIG -->|③| CONF_SMS
  IS_CUSTOM --->|yes| CONF_CUSTOM --> カスタム認証成功 --> DONE2

CONF_SMS --> DONE3
CONF_TOTP --> DONE4
```

`CONTINUE_SIGN_IN_WITH_MFA_SELECTION`はデフォルト値を設定するわけではなく、
あくまでその回はどちらで認証するかを聞いているだけなので、
デフォルトも変えたい場合は`AdminSetUserMFAPreferenceCommand`などのAPIを別途叩く必要がある