Amazon Cognitoをとりあえず触ってみた編

## はじめに
AWS初心者です。AWSの基本知識をキャッチアップすべく、色々触りながら学習中です。
今回は、アプリの認証やユーザー管理の機能を提供するサービス「Amazon Cognito」を実際に触ってみた内容を記事にしました。自分の理解度確認が主目的になっておりますので、温かい目で見て頂ければ幸いです。

## 目的
今回触るサービスはいわゆるID管理サービスになります。これまで、MicrosoftのEntra ID/AzureAD B2CやOktaのCustomer Identity Cloud(CIC)を触ったことはあるので、概念や仕組みは何となく理解しています。アプリ開発ではMUSTな技術領域のため、AWSでのアプリ開発を見据えて、実際にAmazon Cognitoを触りながら機能や使い勝手を確認してみることにしました。

## 概要
本ページでは、サンプルアプリ（ローカル）からCognitoに接続してサインインする仕組みを構築した手順をまとめています。

## 基礎知識

### ID管理サービス(IDaaS)とは
ざっくりいうと認証系機能を提供するサービスです。この記事を書くうえで復習しようと思って色々サイトを見ていましたが、個人的には[「IDaaSとは何か？」](https://www.sbbit.jp/article/cont1/49879#head1)が端的にまとまっていて分かりやすかったです。


## チュートリアル

### Amazon Cognitoへのアクセス
AWSコンソールにサインインし、Amazon Cognitoのページへ移動します。<br>無料で開始できるボタンを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936702/4ef2b079-ed29-3f3e-6082-c3688f5a001a.png)

※環境は12ヵ月無料枠を利用しています。以下の通り、基本的に無料枠の範囲内で利用可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936702/a14da336-ae30-b45e-1c13-82533eb92e69.png)

### アプリケーションの設定
アプリケーションタイプ、アプリケーション名、サインイン識別子のオプション、サインアップのための必須属性を選択・入力します。
今回は、メールアドレス/パスワードでログインするSPAとして、アプリケーションを定義します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936702/5c1d6b48-b051-f209-a8b0-b09ac45f4bb0.png)

作成すると、「ユーザープール」とクライアントアプリケーションが作成されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936702/09e5e37d-4600-b5e3-3d18-74d3af610ecc.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936702/86d6c98f-a797-8303-1c2e-99621a9ab882.png)

#### メモ
クライアントアプリを作成すると、ユーザープールが自動的に作成され、紐づけされました。ユーザープールはユーザーにサインイン/サインアップ機能を提供するユーザーディレクトリです。Entra IDではディレクトリが一つ、AzureAD B2Cではインスタンス単位でディレクトリが一つ紐づきますが、Amazon Cognitoではユーザープール(ディレクトリ)を複数作成することが可能なので、Webアプリ単位でディレクトリを分けたいユースケースに対応しています。

### ユーザーの作成
ツリーメニューの「ユーザー管理」からログイン用のテストユーザーを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936702/fe2cd664-cfa7-b6a1-d8b8-11bbbe38dc7c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936702/3ab9cb52-8686-ef8d-feba-94fb7dd43596.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936702/a94e5c69-af81-0577-650f-bb5e5317987a.png)

作成されると、入力したメールアドレスに初期パスワードを記載したメールが飛んできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936702/d5c3aa70-5777-84f7-f1ee-434d270c9239.png)

これでCognito側の設定は完了です。

## サンプルアプリの作成

### QuickStepガイドの確認
前述で作成したクライアントアプリケーションのページでアプリに追加する手順とサンプルコードが用意されているのでこれを活用します（サンプルコードにはCognitoの認証エンドポイントやクライアントアプリIDなどが定義されているため、そのままコピって使うことができます）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936702/52efda23-7377-49dd-9430-32262cfd798f.png)

### ベースとなるローカルアプリの準備
今回はReactで作るので、以下のコマンドでシングルアプリのプロジェクトを用意します。
```
npx create-react-app my-react-app
```

### OIDCライブラリのインストール
CognitoのQuickStepガイドにも記載されていますが、以下のコマンドを実行して`oidc-client-ts`と`react-oidc-context`をインストールします。
```
npm install oidc-client-ts react-oidc-context --save
```

### コードの変更
CognitoのQuickStepガイドにあるサンプルコードを参考に以下のようにコードを書き換えます。`<xxx>`の部分は自分が作成したユーザープールのエンドポイントやクライアントアプリケーションIDの値に置き換えてください。サンプルコードをコピーする場合は、値を変更する必要はありません。

```index.ts
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

// 認証するために追加したコード
import { AuthProvider } from "react-oidc-context";

const cognitoAuthConfig = {
  authority: "<cognitoユーザープールのURI>",
  client_id: "<クライアントアプリケーションID>",
  redirect_uri: "<リダイレクトURI>",
  response_type: "code",
  scope: "email openid phone",
};

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  <React.StrictMode>
    <AuthProvider {...cognitoAuthConfig}>
      <App />
    </AuthProvider>
  </React.StrictMode>
);
```
```App.js
import logo from './logo.svg';
import './App.css';

import { useAuth } from "react-oidc-context";

function App() {
  const auth = useAuth();

  const signOutRedirect = () => {
    const clientId = "<クライアントアプリケーションID>";
    const logoutUri = "<logout uri>";
    const cognitoDomain = "<cognitoドメイン>";
    window.location.href = `${cognitoDomain}/logout?client_id=${clientId}&logout_uri=${encodeURIComponent(logoutUri)}`;
  };

  if (auth.isLoading) {
    return <div>Loading...</div>;
  }

  if (auth.error) {
    return <div>Encountering error... {auth.error.message}</div>;
  }

  if (auth.isAuthenticated) {
    return (
      <div>
        <pre> Hello: {auth.user?.profile.email} </pre>
        <pre> ID Token: {auth.user?.id_token} </pre>
        <pre> Access Token: {auth.user?.access_token} </pre>
        <pre> Refresh Token: {auth.user?.refresh_token} </pre>

        <button onClick={() => auth.removeUser()}>Sign out</button>
      </div>
    );
  }

  return (
    <div>
      <button onClick={() => auth.signinRedirect()}>Sign in</button>
      <button onClick={() => signOutRedirect()}>Sign out</button>
    </div>
  );
}
  
export default App;
```

### アプリの実行
ローカルでアプリを起動します。
```
cd my-react-app
npm start
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936702/a9c298cd-8d91-d734-f147-fcd3e921a7c3.png)

「Sign in」ボタンを押下すると、未認証のためCognitoが提供するデフォルトのログイン画面へ遷移します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936702/e8c39d30-5d91-13a7-770d-84f35341772e.png)

前述で作成したテストユーザーのメールアドレスを入力後、初期パスワードを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936702/f1d7c66b-80fd-f3c0-4fd1-699d72b9fbc3.png)

初回ログインでパスワード変更が要求されたため変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936702/64fbbb41-6168-f8fc-c30c-8b4d88218432.png)

ログインに成功し、Cognitoで設定したリダイレクトURI（デフォルト）へアクセスすることができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936702/d059f75a-a5ac-bd87-3e6b-a538adb49116.png)


## まとめ
今回は特に下調べなどはせず、とりあえずCognitoを触って理解することを目的にシンプルなログイン機能を作ってみました。次回は、ログイン設定やMFA、ログインページのカスタマイズなどステップアップしてみようと思います。