Astro✖️Amplify✖️Cognitoでログイン機能を作る

## 成果物
![img_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/3795b5dc-1338-c8fb-1bca-0d292d64ad49.png)
↓
![img_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/47f2388b-f450-e226-1c0b-fc0c10566069.png)

## ログインの仕組みのイメージ

大雑把に下図のイメージの理解です。

![img_understanding_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/586df6f1-2bf4-67e2-1af8-f9bbf8f02d59.png)


## 前提
フロントの実装に焦点を当てています。

Cognitoでユーザープールとアプリケーションクライアントを予め作成して下さい。

「セルフサービスのサインアップ」は有効にして下さい。

ユーザーが自分でユーザー登録出来るように作りたいです。

## ディレクトリ構成（完成時）
ほぼデフォルトのままです。
```
.
├── astro.config.mjs
├── package-lock.json
├── package.json
├── public
│   └── favicon.svg
├── src
│   ├── env.d.ts
│   ├── layouts
│   │   └── Layout.astro
│   └── pages
│       ├── auth.astro
│       └── index.astro
└── tsconfig.json
```

## 手順

1. プロジェクト作成
1. Aamplifyインストール
1. auth.astro作成
1. index.astro変更

## プロジェクト作成

```
npm create astro@latest
```

## Aamplifyインストール

Amplifyライブラリをインストールします。

```
npm install aws-amplify
```

## auth.astro作成

/src/pages/auth.astroを以下の内容で作成します。

```html:/src/pages/auth.astro
---
import Layout from '../layouts/Layout.astro';
---

<Layout title="認証ページ">
	<main>
    <h2>ユーザー登録</h2>
    <form name="signup">
      <div>
        <label for="email">ユーザー名:</label>
        <input type="text" name="user" />
      </div>
      <div>
        <label for="email">メールアドレス:</label>
        <input type="text" name="email" />
      </div>
      <div>
        <label for="password">パスワード:</label>
        <input type="password" name="password" />
      </div>
      <div>
        <input type="submit" value="登録" />
      </div>
    </form>
    <h2>ユーザー検証</h2>
    <form name="confirm">
      <div>
        <label for="email">メールアドレス:</label>
        <input type="text"name="email" />
      </div>
      <div>
        <label for="password">検証コード:</label>
        <input type="text" name="code" />
      </div>
      <div>
        <input type="submit" name="submit" value="検証" />
      </div>
    </form>
    <h2>ログイン</h2>
    <form name="login">
      <div>
        <label for="email">メールアドレス:</label>
        <input type="text"name="email" />
      </div>
      <div>
        <label for="password">パスワード:</label>
        <input type="password" name="password" />
      </div>
      <div>
        <input type="submit" name="submit" value="ログイン" />
      </div>
    </form>
  </main>
</Layout>

<script>

  import { Amplify } from 'aws-amplify';
  import { signIn, signUp, confirmSignUp } from 'aws-amplify/auth'

  Amplify.configure({
    Auth: {
      Cognito: {
        userPoolId: 'ご自身のユーザープールID',
        userPoolClientId: 'ご自身のクライアントID',
      }
    }
  });

  // ユーザー登録
  const formSignUp = document.forms['signup'];

  formSignUp.addEventListener('submit', async e => {
    e.preventDefault();
    const user = formSignUp.elements['user'].value;
    const email = formSignUp.elements['email'].value;
    const password = formSignUp.elements['password'].value;
    const { isSignUpComplete, userId, nextStep } = await signUp({
      username: email,
      password: password,
      options: {
        userAttributes: {
          name: user,
        },
      }
    });
    console.log({ isSignUpComplete, userId, nextStep });
    console.log(`${email}に確認のメールを送りました。`);
  });

  // ユーザー検証
  const formVeryficate = document.forms['confirm'];

  formVeryficate.addEventListener('submit', async e => {
    e.preventDefault();
    const email = formVeryficate.elements['email'].value;
    const code = formVeryficate.elements['code'].value;
    const { isSignUpComplete, nextStep } = await confirmSignUp({
      username: email,
      confirmationCode: code
    });
    console.log({ isSignUpComplete, nextStep });
    console.log(`ユーザー検証成功`);
  });

  // ログイン
  const loginForm = document.forms['login'];

  loginForm.addEventListener('submit', async e => {
    e.preventDefault();
    const email = loginForm.elements['email'].value;
    const password = loginForm.elements['password'].value;

    try {
      const result = await signIn({
        username: email,
        password: password,
      });
      console.log(result);
      console.log(`ログイン成功`);
    } catch (err) {
      console.log(err);
    }

  });
</script>

<style>
  main {
    padding: 0 16px;
    /* width: 640px;
    margin: 0 auto; */
  }
  h2 {
    margin-top: 24px;
  }
  form > div {
    margin-top: 16px;
    display: flex;
  }
  label {
    display: block;
    width: 160px;
  }
</style>
```

## index.astro変更

/src/pages/index.astroを以下のように変更します。

```html:/src/pages/index.astro
---
import Layout from '../layouts/Layout.astro';
---

<Layout title="トップページ">
	<main>
    <p id="greet">こんにちは、ゲストさん</p>
	</main>
</Layout>

<script>
  import { Amplify } from 'aws-amplify';
  import { fetchUserAttributes, fetchAuthSession } from 'aws-amplify/auth'

  Amplify.configure({
    Auth: {
      Cognito: {
        userPoolId: 'ご自身のユーザープールID',
        userPoolClientId: 'ご自身のクライアントID',
      }
    }
  });

  try {
    // ログイン時に保存したアクセストークンを使いCognitoにユーザー情報を取りに行く
    const userInfo = await fetchUserAttributes();
    console.log({ userInfo });
    // アクセストークンを使い、APIGWなどを叩きたい場合は以下でアクセストークンを取得出来る
    const { tokens: { accessToken } } = await fetchAuthSession();
    console.log( accessToken );
    
    document.getElementById('greet').innerText = `こんにちは、${userInfo.name}さん`;
  } catch (err) {
    console.log(err);
  }

</script>

<style>
  main {
    padding: 0 16px;
    /* width: 640px;
    margin: 0 auto; */
  }
  p {
    margin-top: 24px;
  }
</style>
```

## ユーザー登録

npm run devしてauthページ（http\://localhost:4321/auth）にアクセスします。

![img_register.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/58be18e3-5fdd-4503-9e88-01895585148e.png)

## ユーザー検証

ユーザー登録すると、入力したメールアドレス宛に検証コードが届きます。

![img_confirm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/ebfca5a3-15f9-dffc-556d-803f93fd9d49.png)

## ログイン

ログイン成功の文字がConsoleに出力されます。

![img_login.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/ef89f63b-3df5-1e58-8bde-a143831f215b.png)

## ユーザーページ

トップページ（http\://localhost:4321）にアクセスします。

登録したユーザー名で「こんにちは、⚫︎⚫︎さん」と表示されます。

![img_userinfo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/a5c3352a-1ef8-e10a-a035-58150ea902c9.png)

## ユーザー名（⚫︎⚫︎さん）を何処で取得しているか？

ログインするとアクセストークンなどがブラウザのローカルストレージに保存されます。

fetchUserAttributes( )をすると、ローカルストレージよりアクセストークンを取り出して、Cognitoからユーザー情報を取得しているようです。

開発者ツールの「Network」タブにてAccessTokenを送信している事が確認出来ます。

![img_fetchdata_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349281/449d6279-918f-4f97-58cf-e06a191d8454.png)

## アクセストークンを使いAPIGWなどにアクセスしたい

index.astroに記述しましたが、fetchAuthSession( )でアクセストークンを取得出来ます。

アクセストークンをtoString()するとJWTになります。

## おわりに

ざっくり理解するためにシンプルに作ることを目標にしました。

実際のプロジェクトだと、ログインを制御するコンポーネントを作ったりするのかな、と想像しています。一旦、動かして見たかったぐらいの気持ちだったので、ここまでの実装にとどめております。悪しからずです。