【Swift×AWS】iOSアプリにAPI Gatewayを導入してCognitoから指定のユーザーデータを消去する

## 前提
ユーザーの退会機能を作るべく、今までAmplifyを使用していたアプリにAPI Gatewayを追加したサーバレスアプリです。
Amplifyからユーザーの削除APIがあればよかったのですが見つからずで、Lambdaから実現することに。（もしAmplifyからのやり方を知っている人がいたら教えてください笑）
元々はLambdaが使えれば良く、どうやらLambdaを直叩きする方法もあるっぽいんですが、API Gatewayを触ったことがなかったのでそちらを使って実装してみることにしました。
また、今回初めてAPI Gatewayを触るので、とりあえずリクエストだけ実装してレスポンスはデフォルトです。
実装が甘いところ等もあると思いますがご容赦ください。

## 構成
構成はこんな感じ。これをAlamofireを使って叩いていきます。

![Untitled Diagram.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653537/f13f392c-f203-42da-42c8-33ee61852f86.png)

実際の設計にはユーザーの他のデータも消すため、Lambdaの先にDynamo DBなりS3なりがくっついているのですが、今回はCognitoのユーザーを消すことが目的なので割愛。
気が向いたらそちらも記事にします。

以下、各所説明。

### ◆API Gateway
今回の主役です。APIがユーザー側から叩かれたらIDトークンを検証して、トークンの中にあるsub値をLambdaに渡します。そしてそれに応じてレスポンスをユーザー側に返していきます。

### ◆AWS Lambda
API Gatewayから呼ばれたら、送られてきたsub値を元に該当するcognitoユーザーを消去します。

## 実装方法
### ◆Lambda
API Gateway実装時に、Lambda関数を指定するため、先に該当するLambda関数を作成しておく。
①Lambda関数を作成する。今回はNode.jsを使用して実装しますが、お好きなのを選択してください。
関数名は今回は"delete_user"とします。

![スクリーンショット 2022-01-28 19.16.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653537/ec1d8061-dd5b-6d5b-aa12-6ec39f235ba2.png)

②関数を作成したら、Lambda関数がcognitoにアクセスできるようにロールにポリシーを振る。
設定タブにあるロールをクリック。
![スクリーンショット 2022-01-28 19.19.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653537/d3cdc069-26fa-a593-8463-dba3a264bc30.png)

③「ポリシーをアタッチします」をクリックし、「AmazonCognitoPowerUser」をアタッチ。
![スクリーンショット 2022-01-28 19.21.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653537/7f16ad08-c720-fca1-d960-83b3c2ae75a3.png)

④コードを実装する。私は以下のコードで実装しました。

```js
let aws = require("aws-sdk");
exports.handler = async (event) => {
    // eventから削除するCognitoユーザーのユーザー名を格納
    let userName = event.context.sub;

    // Cognitoユーザーの無効化メソッドをコール
    await disalbeCognito(userName);

    const response = {
        statusCode: 200,
    };
    return response;
};

// Cognito情報を無効化し、ログインできないようにする 
async function disalbeCognito(username){
    // Cognitoを使う準備
    aws.config.update({
        region: 'ap-northeast-1',
    });
    const cognito = new aws.CognitoIdentityServiceProvider({
        apiVersion: '2016-04-18'
    });
    // 対象のCognitoユーザープールID
    const user_pool_id = "ap-northeast-1_xxxxxxxx";

    // ユーザー削除
    try {
        await cognito.adminDeleteUser({
            UserPoolId: user_pool_id,
            Username: username
        }).promise();
        console.log("Success! userName : " + username);
    } catch (err) {
        console.log("Failed! userName : " + username);
        if (err.code == 'UserNotFoundException') {
            // ユーザープールにユーザーが存在していない場合
            console.log('UserNotFoundException');
        } else {
            // その他のエラー
            console.log(err, err.stack);
        }
        throw err;
    }
   
}
```
使用するならコピペで大丈夫ですが、一箇所注意点があるのでお伝えしておきます。
`let userName = event.context.sub;`
4行目のこちらですが、これはAPI Gatewayの中にあるJSONの影響によってこうなっています。JSONの形式はAPI Gatewayの方で載せてある、そちらを確認してみてください。


こちらでデプロイしてLambdaは完了です。

### ◆API Gateway
①AWSコンソール画面からAPI Gatewayを開き、REST APIの構築を選択。
![スクリーンショット 2022-01-28 19.06.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653537/d153db20-ca63-6627-f475-f8ed0c04cec0.png)


②プロトコル-REST、新しいAPIの作成を選択、API名（わかりやすい名前）を入力し、APIの作成をクリック。
![スクリーンショット 2022-01-28 19.08.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653537/a2a99a55-d263-ec61-d56f-62569872075a.png)


③アクションをクリックしメソッドの作成を選択。
![スクリーンショット 2022-01-28 19.10.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653537/25193e26-845f-e374-3b68-3c305e59ffe8.png)

④作成されたメソッドを選択し、該当するものを選択。今回はユーザーを削除したいため「DELETE」を選択。その後、チェックマークをクリック。
![スクリーンショット 2022-01-28 19.11.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653537/17305c0c-5452-453c-f2df-6b4ba7f8b0ad.png)

⑤作成されたメソッドのセットアップ。ここで先ほど作成したLambdaを指定し、保存をクリック。
![スクリーンショット 2022-01-28 19.46.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653537/5195c559-36bc-cc78-9ada-1d5c813a4f94.png)

⑥"オーソライザー"タブを選択し、「新しいオーソライザーの作成」を選択し、作成。
![スクリーンショット 2022-01-28 19.49.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653537/de04d1f2-3b78-d344-0235-ff0bcdcacc7f.png)
※ここで、消去したいユーザーが存在するユーザープールを選択。
※また、トークンのソースは"Authorization"を入力したが、値はお好きなもので。


⑦リソース→作成したメソッド→メソッドリクエストをクリックし、"認可"の右側の鉛筆マークから⑥で作成したおーそライザーを選択し、チェック。
![スクリーンショット 2022-01-28 19.53.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653537/37dd5f78-0b1c-6136-a467-2692c2bc6733.png)

⑧統合リクエスト内下部のマッピングテンプレートから、「マッピングテンプレートの追加」を選択、Content-typeに"application/json"と入力しチェック。
![スクリーンショット 2022-01-28 19.56.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653537/f47177a8-9286-f760-36b4-b20cd920a852.png)

⑨テンプレートに以下のコードを入力し保存。

```json
{
	"context" : {
		"sub" : "$context.authorizer.claims.sub"
	}
}
```

上記により、IDトークン内のsubの値を取り出してLambdaに渡すことができます。
ここのJSONの形がcontext : { sub : {} } なので、Lambdaのeventの後にcontext.subと記述しています。

⑩アクション→APIのデプロイを選択し、新しくステージを作成してデプロイすれば、完了です。
![スクリーンショット 2022-01-28 20.03.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653537/6fda5e3f-8805-7794-80d1-542791ca31ce.png)

※この手順で作るURLの形は決まっていて、以下のようになっています。

`https://{restapi_id}.execute-api.{region}.amazonaws.com/{stage_name}/`

そのため、stage名もある程度わかりやすいor命名規則を設けた方が良いでしょう。

### ◆Swift
Swift側の実装はメインではないので、さっくりコードだけ載せようと思います。

```swift
    public func deleteAPI( completion: (()->())?=nil) {
        let urlString = "https://{restapi_id}.execute-api.{region}.amazonaws.com/{stage_name}/"
        let defaultHeader = ["Authorization" : "Bearer \(cognito.idToken)"] as HTTPHeaders
        Alamofire.request(urlString, method: .delete, headers: defaultHeader)
               .responseJSON() { (response) in
                   switch response.result {
                   case .success:
                       print("success")
                       break
                   case .failure:
                       print("failure")
                       break
                   }
        }
    }
```

以上で、実装終了です。

## 感想
駆け足でしたが、いかがだったでしょうか。
分かりくいところもあったと思いますが、筆者もそこまで深く理解できているわけではないのでご容赦ください。
何かありましたら、コメントで教えてくださると助かります。

API Gatewayは初めて触ってみたのですが、使いやすくて良いですね。何より便利です。
サーバレスってことでAPI Gatewayが使えてくると元々使えているAmplifyも使ってバックエンド側もガンガン構築できそうです。


それでは。