AWS Cognitoで始める認証の第一歩。

## はじめに

ここ最近、認証周りを覚えないといけないような事が多く、しばらくCognitoをいじってみる記事を書いていきたいと思います！

Cognitoは「認証」「認可」のサービスのようですが、まずは「認証」から確認していきます。

## 認証について。

認証（本人確認）について、少しまとめてみます。

認証は２種類あると考えられます。

### １：あなたは誰ですか？（身元確認）

どこに住んでいて、何歳で、性別は？？といった確認を`身元確認`と呼びます。

身元確認はサービス初回利用時に実施し、`実在性`を確認します。

アカウント登録に個人情報を入力したりするやつです！

### ２：あなたは本当にその人ですか？（当人認証）

今サービスを利用しようとしているあなたが身元確認した当人であることの確認を`当人認証`と呼びます。

当人認証は、ログインや重要操作時のパスワード確認など、サービス利用時に都度行います。

この確認方法は３種類ほどあります。

| 認証の種類 | 説明 | 例 |
|------------|------|-----|
| 知識認証 | 当人しか知り得ない情報で認証 | パスワード、PIN、秘密の質問 |
| 所有物認証 | 当人しか保持していないもので認証 | セキュリティトークン、スマートカード、ワンタイムパスワード生成器 |
| 生体認証 | 当人の身体的な特徴で認証 | 指紋認証、顔認証、静脈認証、虹彩認証 |

この３つの認証のうち、複数を利用する多要素認証が望ましいです。

認証の疎かにすると、不正利用・情報漏洩等、大ダメージを受ける事故になりますが、これらを１から実装するのはとっても大変です。

こういった大変だけど直接ビジネス価値を生まないものは、既存のサービスを活用するのが望ましいです。

そこで、Cognito等の認証サービス（IDaaS）の出番です！

## Cognitoで「認証」を作ってみる。

前置きが長くなったけど、早速作ってみます。

Cognitoにおける「認証」は`ユーザプール`の機能を使って実現するみたいです。

> ユーザープールは、セルフサービスと管理者主導の両方によるユーザー作成、管理、認証を行うユーザーディレクトリです。

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/what-is-amazon-cognito.html#what-is-amazon-cognito-user-pools

また、以前からあったHosted UIよりもカスタマイズ性が向上した、マネージドログインの機能も使ってみます！

https://aws.amazon.com/jp/about-aws/whats-new/2024/11/amazon-cognito-managed-login/

### ユーザプールの作成

ユーザプールを作ってみます。

今回は、Reactを使ってみるので`シングルページアプリケーション(SPA)`で作成します。

`従来のWEBアプリケーション`との違いについては、クライアントシークレットの有無による認証方法の違いがあります。

具体的には認可コードを使ったセキュリティトークン取得時に、`従来のWEBアプリケーション`はサーバサイドで動作するため、クライアントシークレットを使って処理しますが、SPAの場合、シークレットを保持できないため、認可コード横取り問題に対応した`PKCE`といった仕組みを併用して利用します。

オプションは適当に。

![スクリーンショット 2025-04-11 17.38.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894042/bd191d63-a01e-4322-9dd0-c054cb63463c.png)

リターンURLは、マネージドログインを使ったログイン、ログアウト後に遷移するURLを設定します。
一旦、`http://localhost:3000`を指定します。

![スクリーンショット 2025-04-11 18.07.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894042/a44252db-d3ab-44f6-ba50-0ad2ccbe98bf.png)

一旦これでOKです。

### フロントの作成・・・の前に。

今回はAIを使ってシンプルなページを作ってみましたが、最初のページのテンプレートってどう作ればいいんだろう？

Amplify周りに詳しくないかつ、Amplifyのできることが多すぎて沼っぽいので、今回はあまり触れずに進めます。

:::note warn
警告
なので、あまりフロントソースは参考にならないかもです。
:::

なお、完成品は以下にアップロードしています。

https://github.com/SatoshiMoriyama/cognito-managed-login-sample

### ユーザプールの紐付け

`'aws-amplify'`を使って、先ほど作ったユーザプールを利用するために、以下の環境変数をそれぞれ設定します。

| 項目名 | 設定内容 | 例 |
| --- | --- | --- |
| REACT_APP_REGION | AWSリージョン | ap-northeast-1 |
| REACT_APP_USER_POOL_ID | ユーザープールID | ap-northeast-1_xxxxxxxxx |
| REACT_APP_USER_POOL_WEB_CLIENT_ID | アプリクライアントID | xxxxxxxxxxxxxxxxxxxxxxxxxx |
| REACT_APP_COGNITO_DOMAIN | Cognitoドメイン | your-domain-prefix.auth.ap-northeast-1.amazoncognito.com |
| REACT_APP_REDIRECT_SIGN_IN | サインイン後のリダイレクトURL（開発環境） | http://localhost:3000/ |
| REACT_APP_REDIRECT_SIGN_OUT | サインアウト後のリダイレクトURL（開発環境） | http://localhost:3000/ |

```js:src/index.js
import { Amplify } from 'aws-amplify';
import config from './config';

// Amplify設定
Amplify.configure({
  Auth: {
    Cognito: {
      userPoolId: config.cognito.userPoolId,
      userPoolClientId: config.cognito.userPoolWebClientId,
      loginWith: {
        oauth: {
          domain: config.cognito.oauth.domain,
          scopes: config.cognito.oauth.scope,
          redirectSignIn: [config.cognito.oauth.redirectSignIn],
          redirectSignOut: [config.cognito.oauth.redirectSignOut],
          responseType: config.cognito.oauth.responseType
        }
      }
    }
  }
});
```

```js:src/config.js
// AWS Cognitoの設定
const config = {
  cognito: {
    region: process.env.REACT_APP_REGION,
    userPoolId: process.env.REACT_APP_USER_POOL_ID,
    userPoolWebClientId: process.env.REACT_APP_USER_POOL_WEB_CLIENT_ID,
    oauth: {
      domain: process.env.REACT_APP_COGNITO_DOMAIN,
      scope: ['email', 'openid', 'aws.cognito.signin.user.admin'],
      redirectSignIn: process.env.REACT_APP_REDIRECT_SIGN_IN,
      redirectSignOut: process.env.REACT_APP_REDIRECT_SIGN_OUT,
      responseType: 'code' // authorization codeグラントタイプを使用
    }
  }
};

export default config;
```
### 認証まわり

`'aws-amplify/auth'`が便利すぎて、僅かな実装で実現できました。

少しだけ紹介します！

#### マネージドログインへのリダイレクト。

これだけで、マネージドログインサイトへリダイレクトし、操作し終わると元のページに戻ってきます。

```js:App.js
    await signInWithRedirect({
    options:{
      lang : "ja"
    }
```

なお、`サインイン`とかの文言は特に指定しないと英語なので、`ja`を指定しています。

![スクリーンショット 2025-04-11 19.46.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894042/2b8c3613-7864-4977-9e15-7fdc3ea70097.png)


#### ログイン状態の確認

`getCurrentUser()`が成功したらログイン済み、例外がスローされたら未認証状態です。

```js:App.js
    try {
      const currentUser = await getCurrentUser();
      console.log('ログイン状態です');
    } catch (err) {
      console.log('未認証状態です');
    }
```

#### ログアウト

これだけ。

この時に設定したリダイレクトが行われます。

```js:App.js
await signOut()
```

#### ユーザーの属性情報、認証セッション取得

ここも全部関数をそれぞれ呼び出すのみです。

```js:App.js
const attributes = await fetchUserAttributes();
const session = await fetchAuthSession();
```

`attributes.email`みたいな形で、メアドを取得できたり、`session.tokens.idToken`といった形でIDトークンを参照できたりします。

![スクリーンショット 2025-04-12 3.39.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3894042/ecec0c10-03ba-44a4-ba4a-a72abce038de.png)

## さいごに

Cognitoのマネージドログインを使った認証サービス実装自体はとっても簡単にできました！

ただ、今回Amplifyホスティングを使って動作確認してましたが、こっちが複雑でまだよくわかってない感じです。

以降は、多要素認証やSSOなども検証していきます！