Cognito+API Gateway+Lambdaでユーザ作成APIを作りたいんだー！

AWSの認定は保有していますが
実践できていない主なサービスがサーバレスまわり…

ということで勉強を兼ねて、簡単なAPIを作成してみます。

#やりたいこと
APIでユーザ作成
- ログイン名 と パスワードをリクエストボディに指定しAPIをコールするだけでユーザ作成する。
- メールアドレス/電話番号の認証は不要。


#参考
[LambdaでCognito認証(ユーザー作成)](https://qiita.com/minmax/items/8c2aa57b76e09b8192ed)


#やってみよう！
###ユーザプールの作成
こんな私でもCognitoでユーザ管理をするにはユーザプールを作成しなければならないことは知っている。

属性は必要最低限を指定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613712/1a254b24-c952-5ccf-d650-2f2e400d01f9.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613712/cdf97f39-fb52-56ac-96f1-3ad8a2f9320d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613712/47471509-1956-12f8-db90-4c76fa7fdc71.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613712/7881da58-e77a-bf63-a3a6-76f59f1806e8.png)

認証フローはよくわからないため全てチェック。※今後勉強します
ALLOW_USER_PASSWORD_AUTH　が絶対必要
「クライアントシークレットを作成」にはチェックを入れないこと。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613712/858b24a0-ca09-f2f7-52c7-97faf84f3937.png)


###Lambda用IAMロール
Lambdaの基本的なポリシーに加えて、「AmazonCognitoPowerUser」をアタッチする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613712/82abf27b-67d0-4196-1cd5-02da468bbc59.png)


###ユーザ作成用のLambdaを作成
今回、ソースコード内のアプリクライアントID,ユーザプールIDはLambdaの環境変数にセットしています。
作成するユーザはすぐに使える状態にしたかったので、adminConfirmSignUpを利用して確認済み（CONFIRMED)のステータスに変更しています。
バリデーションやエラー処理は適当なので、適宜変更をお願いします。

```index.js
'use strict';

const AWS = require('aws-sdk');
const cognito = new AWS.CognitoIdentityServiceProvider();

/**
 * SignUp
 */
exports.handler = async (event, context) => {
  const response = {};
  let loginName;
  let password;
  console.log(event);
  
  // Validation
  if (!(event.body) || !('loginName' in JSON.parse(event.body)) || !('password' in JSON.parse(event.body))) {
    response["statusCode"] = 400;
    return response;
    
  } else {
    let reqestBody = JSON.parse(event.body);
    
    loginName = reqestBody["loginName"];
    password = reqestBody["password"];
  }

  // SignUp parameters
  const params = {
    ClientId: process.env['APP_CLIENT_ID'],
    Username: loginName, 
    Password: password
  };
  
  response["headers"] = {"Access-Control-Allow-Origin" : "*", "Content-Type" : "application/json"};

  // SignUp
  try {
      const user = await cognito.signUp(params).promise();
      console.log('User sign up success!!!', JSON.stringify(user, null, 4));
      
      
      const confirmParams = {};
      confirmParams['UserPoolId'] = process.env['USER_POOL_ID'];
      confirmParams['Username'] = loginName;
      const result = await cognito.adminConfirmSignUp(confirmParams).promise();
      
      console.log('result : ' + JSON.stringify(result));
      
      response["statusCode"] = 201;
      response["body"] = JSON.stringify({"loginName" : loginName});
      return response;
  }
  catch(err) {
      console.log('User sign up failed...' + err);
      
      if (err.code == 'UsernameExistsException') {
          response["statusCode"] = 409;
      } else if (err.code == 'InvalidPasswordException') {
          response["statusCode"] = 400;
      } else {
          response["statusCode"] = 500;
      }
      
      return response;
  }
};
```

###Lambdaのテスト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613712/21308b20-beb9-2068-b731-625eeccc81ab.png)

テストを実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613712/c2cac66c-0f36-a314-f94e-ed66974b5a33.png)

ユーザプールにユーザが作成された。
ステータスもCONFIRMEDになっている。めでたい！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613712/d6b7bf72-7b8c-5d36-3c92-2b94cc0bd958.png)



### API Gatewayの作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613712/43359e6c-5ec0-869c-cf85-2ecad9381d77.png)

「Lambdaプロキシの統合」にチェックを入れ、作成したLambda関数を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613712/c4db270a-35e0-9980-6a27-43aed0738754.png)

API Gatewayからのテスト実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613712/259e9d60-8ac2-2549-68d5-301a630398b9.png)

こちらもユーザが作成された。めでたい！！！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613712/8b6be0a0-d071-2b6e-5a5f-8400496e5ee7.png)


その後、メールアドレスや電話番号が指定された場合はその値をCognitoに登録するソースへ編集しました。

```index.js
'use strict';

const AWS = require('aws-sdk');
const cognito = new AWS.CognitoIdentityServiceProvider();

/**
 * SignUp
 */
exports.handler = async (event, context) => {
  const response = {};
  let loginName;
  let password;
  let userAttributes;
  console.log(event);
  
  // Validation
  if (!(event.body) || !('loginName' in JSON.parse(event.body)) || !('password' in JSON.parse(event.body))) {
    response["statusCode"] = 400;
    return response;
    
  } else {
    let reqestBody = JSON.parse(event.body);
    
    loginName = reqestBody["loginName"];
    password = reqestBody["password"];
    userAttributes =[];
    
    if('email' in JSON.parse(event.body)) {
      userAttributes.push({'Name':'email','Value':reqestBody["email"]});
    }
    if('phone' in JSON.parse(event.body)) {
      userAttributes.push({'Name':'phone_number','Value':reqestBody["phone"]});
    }
  }

  // SignUp parameters
  const params = {
    ClientId: process.env['APP_CLIENT_ID'],
    Username: loginName, 
    Password: password,
    UserAttributes:userAttributes
  };
  
  response["headers"] = {"Access-Control-Allow-Origin" : "*", "Content-Type" : "application/json"};

  // SignUp
  try {
      const user = await cognito.signUp(params).promise();
      console.log('User sign up success!!!', JSON.stringify(user, null, 4));
      
      
      const confirmParams = {};
      confirmParams['UserPoolId'] = process.env['USER_POOL_ID'];
      confirmParams['Username'] = loginName;
      const result = await cognito.adminConfirmSignUp(confirmParams).promise();
      
      console.log('result : ' + JSON.stringify(result));
      
      response["statusCode"] = 201;
      response["body"] = JSON.stringify({"loginName" : loginName});
      return response;
  }
  catch(err) {
      console.log('User sign up failed...' + err);
      
      if (err.code == 'UsernameExistsException') {
          response["statusCode"] = 409;
      } else if (err.code == 'InvalidPasswordException') {
          response["statusCode"] = 400;
      } else {
          response["statusCode"] = 500;
      }
      
      return response;
  }
};
```


#感想
とりあえず動くものはできたので一安心。
サインアップは出来たけど確認済みへのステータス変更でエラーになった場合にはリトライで再チャレンジなのか…？
ロールバックがないので工夫は必要である。


#今後の宿題
- [APIでユーザ認証をし、アクセストークンを取得する](https://qiita.com/seobomi/items/5dd1db15a7c81675aacf)
- APIでユーザ情報を取得する