AWS CognitoでWebアプリにログイン機能を追加する — User PoolsとIdentity Poolsの使い分け

Webアプリに「サインアップ」と「サインイン」を実装しようとしたとき、AWSコンソールを開くと『Cognito User Pools』と『Cognito Identity Pools』という2つの選択肢が並んでいる。どちらを使えばいいのか迷うのは当然で、名前が似ているせいで混同しやすい。結論から言うと、ユーザーのログイン管理にはUser Poolsを使う。Identity Poolsはまったく別の目的のサービスだ。

TL;DR — AWS Cognitoの使い分け早見表

目的 使うべきサービス 主な機能
サインアップ / サインイン / ユーザー管理 Cognito User Pools ユーザーDB、JWT発行、MFA、パスワードポリシー
AWSリソース(S3, DynamoDBなど)への一時的アクセス Cognito Identity Pools フェデレーテッドID、STS一時認証情報の払い出し
ログイン後にAWSリソースも直接操作させたい 両方を組み合わせる User PoolsでJWT取得 → Identity Poolsで一時認証情報に交換

AWS Cognito User Poolsの仕組みを理解する

User Poolsは、アプリケーション専用のユーザーディレクトリだ。メールアドレスとパスワードでサインアップしたユーザーの情報を保持し、認証が成功するとOpenID Connect準拠のJWTトークン(IDトークン、アクセストークン、リフレッシュトークン)を発行する。バックエンドAPIはこのJWTを検証するだけでよく、セッション管理の複雑さをCognitoに委譲できる。

sequenceDiagram participant Browser as ブラウザ participant UserPool as Cognito User Pools participant API as バックエンドAPI Browser->>UserPool: サインアップ (メール/パスワード) UserPool-->>Browser: 確認コード送信 Browser->>UserPool: confirm-sign-up (確認コード) UserPool-->>Browser: 確認完了 Browser->>UserPool: initiate-auth (USER_SRP_AUTH) UserPool-->>Browser: IDトークン / アクセストークン / リフレッシュトークン Browser->>API: APIリクエスト (Authorization: IDトークン) API->>UserPool: JWKSエンドポイントでトークン検証 UserPool-->>API: 検証結果 API-->>Browser: レスポンス
  1. サインアップ / サインイン: ブラウザからCognito User Poolsへ認証リクエストを送る。
  2. JWT発行: 認証成功後、IDトークン・アクセストークン・リフレッシュトークンの3種類が返される。
  3. APIアクセス: フロントエンドはAuthorizationヘッダにIDトークンを付けてバックエンドAPIを呼ぶ。
  4. JWT検証: API GatewayまたはバックエンドがUser PoolsのJWKSエンドポイントを使ってトークンを検証する。

Identity Poolsはこのフローとは無関係だ。Identity Poolsの役割は、認証済みユーザー(またはゲストユーザー)にAWS STSの一時的な認証情報を払い出すことであり、ユーザーのパスワードやプロフィールを管理する機能は持っていない。

User Poolsを『アプリのユーザーDB + 認証サーバー』、Identity Poolsを『AWSリソースへの入場券発行機』と考えると役割が整理しやすい。

AWS Cognito User Poolsでサインアップ・サインインを実装する

以下の手順でUser Poolsを作成し、アプリクライアントを設定する。CLIで一連の操作を完結させることができる。

Step 1: User Poolsを作成する

まずUser Poolsを作成する。ここでパスワードポリシーやMFAの設定が決まる。作成後に返されるUserPoolIdは以降の手順で必要になるため、必ず記録しておく。

aws cognito-idp create-user-pool \
  --pool-name MyAppUserPool \
  --policies 'PasswordPolicy={MinimumLength=8,RequireUppercase=true,RequireLowercase=true,RequireNumbers=true,RequireSymbols=false}' \
  --auto-verified-attributes email \
  --region us-east-1

--auto-verified-attributes emailを指定すると、サインアップ時にメールアドレス確認コードが自動送信される。本番環境では--mfa-configurationオプションでMFAを有効化することを強く推奨する。

Step 2: アプリクライアントを作成する

User Poolsを作成しただけではアプリから認証できない。アプリクライアントを作成することで、フロントエンドやバックエンドがUser Poolsと通信するためのクライアントIDが払い出される。--no-generate-secretはSPAやモバイルアプリなどクライアントシークレットを安全に保管できない環境向けの設定だ。

aws cognito-idp create-user-pool-client \
  --user-pool-id us-east-1_XXXXXXXXX \
  --client-name MyAppClient \
  --no-generate-secret \
  --explicit-auth-flows ALLOW_USER_SRP_AUTH ALLOW_REFRESH_TOKEN_AUTH \
  --region us-east-1

Step 3: ユーザーのサインアップとメール確認

アプリクライアントIDが取得できたら、実際にユーザーを登録できる。以下はCLIでサインアップを実行する例だ。実際のアプリではAWS Amplify LibrariesやCognito Hosted UIを使うことが多いが、動作確認にはCLIが便利だ。

aws cognito-idp sign-up \
  --client-id YOUR_CLIENT_ID \
  --username user@example.com \
  --password 'MyP@ssword1' \
  --region us-east-1

サインアップ後、登録メールアドレスに確認コードが届く。そのコードで確認を完了させる。

aws cognito-idp confirm-sign-up \
  --client-id YOUR_CLIENT_ID \
  --username user@example.com \
  --confirmation-code 123456 \
  --region us-east-1

Step 4: サインインしてJWTを取得する

メール確認が完了したユーザーでサインインする。USER_SRP_AUTHフローはパスワードをネットワーク上に平文で送らないSRP(Secure Remote Password)プロトコルを使用する。CLIでの簡易確認にはUSER_PASSWORD_AUTHフローも使えるが、本番アプリではUSER_SRP_AUTHを使うこと。

aws cognito-idp initiate-auth \
  --auth-flow USER_PASSWORD_AUTH \
  --auth-parameters USERNAME=user@example.com,PASSWORD='MyP@ssword1' \
  --client-id YOUR_CLIENT_ID \
  --region us-east-1

成功するとAuthenticationResultの中にIdTokenAccessTokenRefreshTokenが返される。バックエンドAPIの認証にはこのIdTokenをAuthorizationヘッダに付与する。

よくある誤解 — Identity PoolsをユーザーDBとして使おうとした場合

「Identity Poolsにもユーザーを追加できるのでは?」と考えてAWSコンソールを操作すると、Identity Poolsにはユーザー登録フォームが存在しないことに気づく。Identity Poolsが管理するのはIAMロールへのマッピングであり、ユーザーのパスワードやメールアドレスを保持する仕組みではない。

graph LR User["ユーザー"] --> UP["Cognito User Pools
認証 / JWT発行"] UP -- "JWTトークン" --> IP["Cognito Identity Pools
JWT → 一時認証情報に交換"] IP -- "STS一時認証情報" --> STS["AWS STS"] STS -- "IAM一時認証情報" --> AWSRes["AWSリソース
S3 / DynamoDB など"] UP -- "JWTトークン" --> APIGW["API Gateway
Cognitoオーソライザー"] APIGW --> Backend["バックエンドAPI"]
  1. 認証フェーズ: User Poolsがユーザー認証を担当し、JWTを発行する。
  2. 認証情報交換フェーズ: Identity PoolsはUser PoolsのJWTを受け取り、STSに一時的なIAM認証情報を要求する。
  3. AWSリソースアクセス: フロントエンドはSTSから受け取った一時認証情報を使ってS3やDynamoDBに直接アクセスする。

ユーザーのサインアップ・サインインだけが目的なら、Identity Poolsは不要だ。S3バケットへの直接アップロードなど、フロントエンドからAWSリソースを直接操作させたい場合にのみ、User PoolsとIdentity Poolsを組み合わせる構成を検討する。

Cognito User Pools管理に必要なIAMポリシー

CI/CDパイプラインや管理スクリプトからUser Poolsを操作する場合、最小権限の原則に従ったIAMポリシーが必要だ。CreateUserPoolはリソースレベルの権限制御をサポートしていないため、このアクションだけResource: "*"が必要になる。その他のアクションは特定のUser Pool ARNに絞ることができる。

🔽 IAMポリシーサンプルを展開する
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CognitoCreateUserPool",
      "Effect": "Allow",
      "Action": [
        "cognito-idp:CreateUserPool"
      ],
      "Resource": "*"
    },
    {
      "Sid": "CognitoManageSpecificUserPool",
      "Effect": "Allow",
      "Action": [
        "cognito-idp:DescribeUserPool",
        "cognito-idp:UpdateUserPool",
        "cognito-idp:DeleteUserPool",
        "cognito-idp:CreateUserPoolClient",
        "cognito-idp:DescribeUserPoolClient",
        "cognito-idp:AdminCreateUser",
        "cognito-idp:AdminDeleteUser",
        "cognito-idp:AdminGetUser",
        "cognito-idp:ListUsers"
      ],
      "Resource": "arn:aws:cognito-idp:us-east-1:123456789012:userpool/us-east-1_XXXXXXXXX"
    }
  ]
}

CreateUserPoolResource: "*"にしなければならない理由は、User Pool作成時点ではARNがまだ存在しないためだ。作成後の操作(DescribeUserPool、AdminCreateUserなど)は特定のARNに絞ることができ、最小権限の原則を維持できる。

実際の障害パターン — 『トークンは返ってくるのにAPIが401を返す』

サインインは成功してIDトークンが取得できているのに、API Gatewayが401を返し続けるケースがある。最初はCognitoの設定を疑うが、実際にはAPI GatewayのCognitoオーソライザー設定の問題であることが多い。

具体的には、オーソライザーに設定したUser Pool ARNと、トークンを発行したUser Poolが一致していないケースだ。開発環境と本番環境でUser Poolsを分けているときに、デプロイ時に環境変数の差し替えを忘れると発生する。CloudFormationやTerraformでインフラを管理していても、API Gatewayのオーソライザーだけをコンソールでアドホックに変更していると見落としやすい。

確認コマンドはシンプルだ。

aws apigateway get-authorizer \
  --rest-api-id YOUR_API_ID \
  --authorizer-id YOUR_AUTHORIZER_ID \
  --region us-east-1

返ってきたproviderARNsの値と、トークンを発行したUser PoolのARNを突き合わせる。一致していなければオーソライザーを更新する。

aws apigateway update-authorizer \
  --rest-api-id YOUR_API_ID \
  --authorizer-id YOUR_AUTHORIZER_ID \
  --patch-operations op=replace,path=/providerARNs/0,value=arn:aws:cognito-idp:us-east-1:123456789012:userpool/us-east-1_XXXXXXXXX \
  --region us-east-1

JWTの中身をjwt.ioでデコードしてissクレームを確認すると、どのUser Poolが発行したトークンかが一目でわかる。401が出たらまずここを見る習慣をつけると診断が早い。

AWS Cognito User Poolsのまとめと次のステップ

WebアプリのサインアップとサインインにはCognito User Poolsを使う。Identity Poolsはユーザーデータベースではなく、認証済みユーザーにAWSリソースへの一時的なアクセス権を付与するための仕組みだ。この2つを混同すると設計が複雑になるだけなので、目的に応じて明確に使い分けることが重要だ。

次のステップとして、以下を参照することを推奨する。

用語集

用語 説明
User Pools Cognitoが提供するユーザーディレクトリ。サインアップ・サインイン・JWT発行を担う。
Identity Pools 認証済みユーザーにAWS STSの一時的な認証情報を払い出す仕組み。ユーザーDBではない。
JWT (JSON Web Token) 認証成功後にUser Poolsが発行する署名付きトークン。IDトークン・アクセストークン・リフレッシュトークンの3種類がある。
SRP (Secure Remote Password) パスワードをネットワーク上に平文で送らずに認証するプロトコル。Cognitoの推奨認証フロー。
アプリクライアント User Poolsに対してアプリが認証リクエストを送るための識別子。クライアントIDとして使用する。

Related Posts

コメント

このブログの人気の投稿

SQS Visibility Timeoutの完全解説:メッセージ二重処理の原因と対策

EC2 SSH接続タイムアウトの原因と修正方法 — セキュリティグループのインバウンドルール完全ガイド

S3パブリックアクセス拒否の原因と解決策:バケットレベルの「Block Public Access」が優先される仕組み