AWS CognitoでMFA = OPTIONALにし、ユーザーごとに有効にする


タイトルの通りです。

普段はnode(typescript)とかでアプリケーションの構築(APIサーバー)してます。
cognitoでユーザーごとにMFA有効にしようとしたらどうすればいいかを検証した備忘録を以下に記します。

# 前提

- cognitoのuserPoolは作ってある
    - emailでのログインを許可してる
    - クライアントも作成してる
- 有効にするのはTOTPでのMFA
- 検証レベルなので、awsCliを使う
    - configはすでに設定してある


# MFA = requiredはuserPool作成時しか設定できない

ドキュメントは以下

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/user-pool-settings-mfa.html

# 必須以外なら変更できる

off <-> optional は可能。

スクリーンショットの通り。
cognito > MFA and verifications にて設定可能。

optionalにし、TOTPにチェックを入れておく。

<img width="1403" alt="cognito_ss_1.png" src="https://qiita-image-store.s3.amazonaws.com/0/213486/04aaf7be-246c-b332-8c66-52a9ede67102.png">

<img width="1417" alt="cognito_ss_2.png" src="https://qiita-image-store.s3.amazonaws.com/0/213486/c6795fe6-8832-2bb3-9099-84242cd79d00.png">

# signupとconfirm

```bash
$ aws cognito-idp sign-up --client-id [clientId] --username [email] --password [password]
{
    "UserConfirmed": false,
    "CodeDeliveryDetails": {
        "Destination": "[一部マスクされたemail]",
        "DeliveryMedium": "EMAIL",
        "AttributeName": "email"
    },
    "UserSub": "[userSub]"
}

$ aws cognito-idp confirm-sign-up --client-id [clientId] --username [email] --confirmation-code [メールで送られてきたconfirmationCode]
```

# まずはログインしてみる

`MFA = off` の時と同じレスポンス

```bash
$ aws cognito-idp admin-initiate-auth --user-pool-id [userPoolId] --client-id [clientId] --auth-flow ADMIN_NO_SRP_AUTH --auth-parameters USERNAME=[email],PASSWORD=[password]
{
    "ChallengeParameters": {},
    "AuthenticationResult": {
        "AccessToken": "[accessToken]",
        "ExpiresIn": 3600,
        "TokenType": "Bearer",
        "RefreshToken": "[refreshToken]",
        "IdToken": "[idToken]"
    }
}
```

# MFAの有効化

```bash
$ aws cognito-idp associate-software-token --access-token [accessToken]
{
    "SecretCode": "[secretCode]"
}
```

googleAuthenticator等で、このコードを登録するか、このコードからQRコードを生成して読み取る

# トークンの検証

```bash
$ aws cognito-idp verify-software-token --access-token [accessToken] --user-code [googleAuthenticator等で発行されてるトークン]
{
    "Status": "SUCCESS"
}
```

# ユーザーにMFA設定をセット

TOTPを有効かつ優先にする設定
（優先もなにもこのuserPoolにはTOTPしか許可してないけど）

```bash
$ aws cognito-idp admin-set-user-mfa-preference --username [userName] --user-pool-id [userPoolId] --software-token-mfa-settings Enabled=true,PreferredMfa=true
```

# 設定完了後にログインしてみる

さきほどのログインとはレスポンスが異なる。
`ログインに挑戦しましょう。成功したらaccessToken渡すよ` 的な状態になってる。
ので、続いてMFAのトークン検証も通れば、accessTokenが返る。

`USER_ID_FOR_SRP` は、 `USER_ID_FOR_SRP === userName === sub` となっているので、uuid的なのが表示されてる。

```bash
$ aws cognito-idp admin-initiate-auth --user-pool-id [userPoolId] --client-id [clientId] --auth-flow ADMIN_NO_SRP_AUTH --auth-parameters USERNAME=[email],PASSWORD=[password]
{
    "ChallengeName": "SOFTWARE_TOKEN_MFA",
    "Session": "[session]",
    "ChallengeParameters": {
        "USER_ID_FOR_SRP": "[uuid的なやつ]"
    }
}
```

# ログイン時のトークン検証

sessionやchallengeNameなど、 `admin-initiate-auth` のレスポンスを使う。

```bash
$ aws cognito-idp admin-respond-to-auth-challenge --user-pool-id [userPoolId] --client-id [clientId] --challenge-name SOFTWARE_TOKEN_MFA --session [session] --challenge-responses USERNAME=[admin-initiate-authのレスポンスのUSER_ID_FOR_SRP],SOFTWARE_TOKEN_MFA_CODE=[googleAuthenticator等で発行されてるトークン]
{
    "ChallengeParameters": {},
    "AuthenticationResult": {
        "AccessToken": "[accessToken]",
        "ExpiresIn": 3600,
        "TokenType": "Bearer",
        "RefreshToken": "[refreshToken]",
        "IdToken": "[idToken]"
    }
}
```

ドキュメント見ても、 `--challenge-responses` のパラメータの記載見つけられなくて困惑した。
エラー吐かせて足りないパラメータ確認するを繰り返した。

各チャレンジに対応した `--challenge-responses` のパラメータの記載が網羅されているものがあれば教えていただけないでしょうか...。

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/user-pool-settings-mfa-totp.html

https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminRespondToAuthChallenge.html

# ちなみに

associate前にsetしようとすると以下のように怒られるので、cognitoAPIの実行順に注意です。

`associate-software-token` => `admin-set-user-mfa-preference` の順番で！

```bash
An error occurred (InvalidParameterException) when calling the AdminSetUserMFAPreference operation: User has not set up software token mfa
```

# typescriptの場合

僕が実際にAPIに組み込む時はこれをtsで使う必要がある。
jsのawsSdkだと以下が同様のcognitoAPI（だと思う）

```typescript
new CognitoIdentityServiceProvider().adminInitiateAuth();
new CognitoIdentityServiceProvider().associateSoftwareToken();
new CognitoIdentityServiceProvider().adminSetUserMFAPreference();
new CognitoIdentityServiceProvider().verifySoftwareToken();
new CognitoIdentityServiceProvider().adminRespondToAuthChallenge();
```