CognitoユーザープールにAWS SDK for JavaScript v3でアクセス その2 - ユーザ登録

https://qiita.com/pilot/items/df8f3091e5ed0e35b4b4

の続き

# Cognitoクライアントの設定確認
1. AWSコンソール→Cognito→MyUserPool→アプリケーションの統合タブ→MyAppClient
1. クライアントIDとクライアントシークレットをメモ ★1

# 登録メール受取用gmailアカウント
今回はCognitoから送られるメールについてなりすましとみなされる件(メールのFromのドメインと実際の送信元IPアドレスが異なる)の対応は行わない
そのため、例えばdocomoメアドだとメールを受け取れない
gmailだったら受け取れるのでgmailアカウントを用意する (新規でも既存でも良い) ★2

# ユーザ登録 (メール送るまで)
1. 作業フォルダに以下のファイルを作成 → 使用クラス https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-cognito-identity-provider/classes/signupcommand.html
    ```sign-up.js
    import { CognitoIdentityProviderClient, SignUpCommand } from "@aws-sdk/client-cognito-identity-provider";
    import { createHmac } from 'crypto';
    
    const cognitoClient = new CognitoIdentityProviderClient({ region: '＜東京リージョンならap-northeast-1＞' });
    
    const email = process.argv[2];
    const password = process.argv[3];
    console.log('email=' + email + ', password=' + password);
    
    const clientId = '＜★1の値＞';
    const clientSecret = '＜★1の値＞';
    const hmac = createHmac('sha256', clientSecret);
    hmac.update(email + clientId);
    const secretHash = hmac.digest('base64');
    
    const response = await cognitoClient.send(new SignUpCommand({ ClientId: clientId, SecretHash: secretHash,  Username: email, Password: password }));
    console.log(response);
    ```
1. 実行 → 再実行するならAWSコンソール→Cognito→MyUserPool→ユーザータブにて当該ユーザを削除する
    ```
    node sign-up.js ＜★2のgmailメアド＞ ＜パスワード(Cognitoのパスワード要件より大文字や記号を混ぜる) ★3＞
    ```
1. gmailに届いたメールより確認コードを取得 ★4
1. AWSコンソール→Cognito→MyUserPool→ユーザータブにて当該ユーザの状態を確認

# ユーザ登録 (登録完了まで)
1. 作業フォルダに以下のファイルを作成 → 使用クラス https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-cognito-identity-provider/classes/confirmsignupcommand.html
    ```confirm-sign-up.js
    import { CognitoIdentityProviderClient, ConfirmSignUpCommand } from "@aws-sdk/client-cognito-identity-provider";
    import { createHmac } from 'crypto';
    
    const cognitoClient = new CognitoIdentityProviderClient({ region: '＜東京リージョンならap-northeast-1＞' });
    
    const email = process.argv[2];
    const confirmationCode = process.argv[3];
    console.log('email=' + email + ', confirmationCode=' + confirmationCode);
    
    const clientId = '＜★1の値＞';
    const clientSecret = '＜★1の値＞';
    const hmac = createHmac('sha256', clientSecret);
    hmac.update(email + clientId);
    const secretHash = hmac.digest('base64');
    
    const response = await cognitoClient.send(new ConfirmSignUpCommand({ ClientId: clientId, SecretHash: secretHash, Username: email, ConfirmationCode: confirmationCode }));
    console.log(response);
    ```
1. 実行
    ```
    node confirm-sign-up.js ＜★2のgmailメアド＞ ＜★4の確認コード＞
    ```
1. AWSコンソール→Cognito→MyUserPool→ユーザータブにて当該ユーザの状態を確認

# おまけ - リファクタリング、確認コード再送
```.env
AWS_ACCESS_KEY_ID=XXX
AWS_SECRET_ACCESS_KEY=XXX
AWS_REGION=XXX
AWS_COGNITO_USER_POOL_ID=XXX
AWS_COGNITO_CLIENT_ID=XXX
AWS_COGNITO_CLIENT_SECRET=XXX
```

```util.js
import * as dotenv from 'dotenv'
dotenv.config();
import { CognitoIdentityProviderClient, ListUsersCommand, SignUpCommand, ConfirmSignUpCommand, ResendConfirmationCodeCommand } from "@aws-sdk/client-cognito-identity-provider";
import { createHmac } from 'crypto';

function env(name) {
    return process.env[name];
}

async function send(command) {
    return (new CognitoIdentityProviderClient({ region: env('AWS_REGION') })).send(command);
}

function getSecretHash(email) {
    return createHmac('sha256', env('AWS_COGNITO_CLIENT_SECRET')).update(email + env('AWS_COGNITO_CLIENT_ID')).digest('base64');
}

async function listUser() {
    return send(new ListUsersCommand({ UserPoolId: env('AWS_COGNITO_USER_POOL_ID') }));
}

async function signUp(email, password) {
    return send(new SignUpCommand({ ClientId: env('AWS_COGNITO_CLIENT_ID'), SecretHash: getSecretHash(email),  Username: email, Password: password }));
}

async function confirmSignUp(email, confirmationCode) {
    return send(new ConfirmSignUpCommand({ ClientId: env('AWS_COGNITO_CLIENT_ID'), SecretHash: getSecretHash(email), Username: email, ConfirmationCode: confirmationCode }));
}

async function resendConfirmationCode(email) {
    return send(new ResendConfirmationCodeCommand({ ClientId: env('AWS_COGNITO_CLIENT_ID'), SecretHash: getSecretHash(email), Username: email }));    
}

export {
    listUser, signUp, confirmSignUp, resendConfirmationCode
}
```

```list-users.js
import { listUser } from './util.js'

const response = await listUser();
console.log(response);
```

```sign-up.js
import { signUp } from './util.js'

const email = process.argv[2];
const password = process.argv[3];
console.log('email=' + email + ', password=' + password);

const response = await signUp(email, password);
console.log(response);
```

```confirm-sign-up.js
import { confirmSignUp } from './util.js'

const email = process.argv[2];
const confirmationCode = process.argv[3];
console.log('email=' + email + ', confirmationCode=' + confirmationCode);

const response = await confirmSignUp(email, confirmationCode);
console.log(response);
```

```resend-confirmation-code.js
import { resendConfirmationCode } from './util.js'

const email = process.argv[2];
console.log('email=' + email);

const response = await resendConfirmationCode(email);
console.log(response);
```

# 今後
認証、ただしSRPだとしんどそうなのでNON SRPで (実際SRPじゃないとどう危ないのかよくわからないのもある)
JWT検証
メアド変更
パスワード変更
Webアプリ化