AWS APIGateway(HTTP API)とCognitoを統合

# やりたいこと
フロントエンド(ここではReactアプリ)から、APIGateway経由でLambda関数を実行するときにCognitoで認証されたユーザーからのリクエストのみをAPIGatewayで受け付けるようにしたい
※備忘録に近い内容です

# 作成手順
###### ①Lambda関数を作成
何でも良いのでLambda関数を作成
とりあえず作成したときに出来上がるHelloLambdaのままとした
(ここでやりたいことはLambdaの中身は関係ないので)
```
export const handler = async (event) => {
  // TODO implement
  const response = {
    statusCode: 200,
    body: JSON.stringify('Hello from Lambda!'),
  };
  return response;
};
```

###### ②APIGateway作成
・HTTP APIを作成
・Authorization設定
　オーソライザーのタイプ：JWT
　IDソース：$request.header.Authorization
　発行者URL：https://cognito-idp.ap-northeast-1.amazonaws.com/{userPoolId}
　対象者：CognitoユーザープールのアプリケーションクライアントIDを入力
 ・CORSの設定（ReactアプリからAPIGatewayを呼び出すため）
　Access-Control-Allow-Origin：* （本番環境の場合は具体的なドメイン名を指定すること）
　Access-Control-Allow-Methods：*
　Access-Control-Allow-Headers：content-type,authorization

# 試しに実行
ブラウザからAPIGatewayのURLをたたいてみる
 -> 401 Unauthorizedが返ってくる。想定通り

# ReactからAPIGateway実行
```
import {
  CognitoUserPool,
  AuthenticationDetails,
  CognitoUser,
} from "amazon-cognito-identity-js";

const poolData = {
  UserPoolId: "ap-northeast-1_xxxxxxxxx", // ユーザープールID
  ClientId: "6b093xxxxxxxxxxxxxxxxxxxx", // アプリクライアントID
};

const userPool = new CognitoUserPool(poolData);

// Cognitoログイン
function signIn(username, password) {
  const authenticationData = {
    Username: username,
    Password: password,
  };
  const authenticationDetails = new AuthenticationDetails(authenticationData);

  const userData = {
    Username: username,
    Pool: userPool,
  };
  const cognitoUser = new CognitoUser(userData);

  cognitoUser.authenticateUser(authenticationDetails, {
    onSuccess: (result) => {
      debugger;
      const accessToken = result.getAccessToken().getJwtToken();
      const idToken = result.getIdToken().getJwtToken();
      console.log("access token: " + accessToken);
      console.log("id token: " + idToken);
      // アクセストークンを使用した処理をここに記述
      alert("ログイン完了" + accessToken);
    },
    onFailure: (err) => {
      console.error(err);
      alert(err.message || JSON.stringify(err));
    },
  });
}

// APIGateway Called
const apiGatewayCall = async (idToken) => {
  debugger;
  try {
    const response = await fetch(
      "https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/endpoint",
      {
        method: "GET",
        headers: {
          Authorization: `Bearer ${idToken}`,
          "Content-Type": "application/json",
        },
      }
    );
    const result = await response.json();
    console.log("result", result);
  } catch (error) {
    console.error("api error", error);
  }
};

export default function LoginForm() {
  const handleLogin = (event) => {
    event.preventDefault();
    const username = event.target.username.value;
    const password = event.target.password.value;
    signIn(username, password);
  };

  const apiCall = (event) => {
    event.preventDefault();
    const idToken = event.target.idToken.value;
    apiGatewayCall(idToken);
  };

  return (
    <>
      <form onSubmit={handleLogin}>
        <input type="text" name="username" required />
        <input type="password" name="password" required />
        <button type="submit">ログイン</button>
      </form>
      <br />
      <form onSubmit={apiCall}>
        id_token=
        <input type="text" name="idToken" required />
        <button type="submit">ApiCall</button>
      </form>
    </>
  );
}
```

# 使い方
①一つ目のformのインプットテキストにCognitoユーザーIDとパスワードを入力してログインボタン押下
②signIn関数が実行されるので、console.logで取得しているaccess tokenまたはid tokenをメモ
③ ②でメモしたtoken(access tokenを使う場合が多い)を二つ目のformのインプットテキストに入力しApiCallボタン押下
④apiGatewayCall関数が実行される。
　正しく実行されていれば、result.messageに「Hello from Lambda!」200が返ってくる
　認証エラーの場合、result.messageに「Unauthorized」401が返ってくる