概要

Amazon Cognitoは、AWSが提供するフルマネージド型のユーザ認証・認可サービスである。
Webアプリケーションやモバイルアプリケーションに、ユーザ登録、ログイン、アクセス制御等の機能を簡単に追加できる。

主な特徴を以下に示す。

  • ユーザ管理
    ユーザ登録、ログイン、パスワードリセット等を自動処理
  • JWT認証
    標準的なJWT (JSON Web Token) を使用した認証
  • MFA対応
    多要素認証 (SMS、TOTP) をサポート
  • ソーシャルログイン
    Google、Facebook、Amazon等のソーシャルプロバイダーと連携可能
  • セキュリティ機能
    パスワードポリシー、アカウントロック、デバイストラッキング
  • スケーラビリティ
    数百万ユーザまで自動スケール


Amazon Cognitoは2つの主要コンポーネントで構成されている。

  • ユーザプール (User Pools)
    ユーザディレクトリとして機能する
    サインアップ、サインイン、トークン発行を管理する
    API Gatewayとの統合が容易である
  • IDプール (Identity Pools)
    AWSリソースへの一時的なアクセス権限を提供する
    認証されたユーザにIAMロールを割り当てる
    S3、DynamoDB等への直接アクセスに使用する


本ページでは、API GatewayでのJWT認証に使用するユーザプールについて説明する。


Amazon Cognitoを使用する利点

開発効率の向上

  • 認証システムの自前実装が不要
    ユーザ登録、ログイン、パスワード管理等の実装が不要
    セキュリティのベストプラクティスが自動適用される
  • トークン管理の自動化
    JWT生成、検証、リフレッシュが自動処理される
    API Gatewayが自動的にトークン検証を行う
  • スケーラビリティ
    ユーザ数の増加に自動対応する
    インフラ管理が不要である


セキュリティ

  • 業界標準のセキュリティ
    bcryptによるパスワードハッシュ化
    HTTPS通信の強制
    トークンの自動期限管理
  • コンプライアンス対応
    GDPR、HIPAA、SOC、ISO準拠
    監査ログの自動記録
  • 攻撃への対策
    ブルートフォース攻撃の検出と防御
    アカウント乗っ取りの検出
    異常なアクティビティの通知


コスト効率

  • 無料枠
    月間アクティブユーザ (MAU) 50,000人まで無料
    小規模アプリケーションは完全無料で運用可能
  • 従量課金
    使用した分だけの支払い
    初期コストやサーバー維持費が不要



主要な概念

ユーザプール

ユーザプールは、ユーザディレクトリであり、以下に示す機能を提供する。

  • ユーザ登録とサインイン
    メールアドレスまたはユーザ名でのサインアップ
    カスタム属性の追加 (最大50個)
  • 認証フロー
    ユーザ名とパスワードによる認証
    SRP (Secure Remote Password) プロトコル
    カスタム認証フローの実装
  • トークン管理
    IDトークン (ユーザ情報を含む)
    アクセストークン (API呼び出し用)
    リフレッシュトークン (トークン更新用)


トークンの種類と用途

Cognitoは3種類のトークンを発行する。

  • IDトークン
    ユーザのアイデンティティ情報を含むJWT
    API Gatewayでの認証に使用する
    ペイロードにユーザ属性 (sub、email等) を含む
    有効期限は1時間 (デフォルト、変更可能)
  • アクセストークン
    APIアクセスの認可に使用するJWT
    スコープ情報を含む
    Cognitoのユーザ情報APIの呼び出しに使用
    有効期限は1時間 (デフォルト、変更可能)
  • リフレッシュトークン
    IDトークンとアクセストークンを再取得するためのトークン
    長期間有効 (デフォルト30日、最大3,650日)
    リフレッシュトークン自体は更新されない


アプリクライアント

アプリクライアントは、ユーザプールと通信するアプリケーションの設定である。

  • クライアントID
    アプリケーションを識別する一意のID
    トークンのAudienceクレームに含まれる
  • クライアントシークレット
    サーバーサイドアプリケーション用の秘密鍵
    SPAやモバイルアプリでは使用しない (秘密を保持できないため)
  • 認証フロー
    ALLOW_USER_PASSWORD_AUTH: ユーザ名とパスワード認証
    ALLOW_USER_SRP_AUTH: SRPプロトコル認証 (推奨)
    ALLOW_REFRESH_TOKEN_AUTH: リフレッシュトークン認証
    ALLOW_CUSTOM_AUTH: カスタム認証



Cognitoユーザプールの作成

AWS CLIでの作成

クライアントから、AWS CLIを使用してユーザプールを作成する。

ユーザプールの作成
# ユーザプールの作成
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 \
    --username-attributes email      \
    --schema '[
      {
        "Name": "email",
        "AttributeDataType": "String",
        "Required": true,
        "Mutable": false
      }
    ]'                      \
    --mfa-configuration OFF \
    --region ap-northeast-1

# 出力例:
# {
#   "UserPool": {
#     "Id": "ap-northeast-1_AbCdEfGhI",
#     "Name": "MyAppUserPool",
#     ...
#   }
# }


パラメータの説明

  • --pool-name
    ユーザプールの名前
  • --policies
    パスワードポリシーを定義する
    MinimumLength: 最小文字数 (6~99)
    RequireUppercase: 大文字を必須にする
    RequireLowercase: 小文字を必須にする
    RequireNumbers: 数字を必須にする
    RequireSymbols: 記号を必須にする
  • --auto-verified-attributes
    自動検証する属性 (email または phone_number)
  • --username-attributes
    ユーザ名として使用する属性
    email を指定すると、メールアドレスでログイン可能になる
  • --schema
    カスタムユーザ属性を定義する
    Name: 属性名
    AttributeDataType: データ型 (String、Number、DateTime、Boolean)
    Required: 必須かどうか
    Mutable: 変更可能かどうか
  • --mfa-configuration
    MFA設定 (OFF、ON、OPTIONAL)


※重要
UserPool IDを控えておく。(後の手順で使用)
例: ap-northeast-1_AbCdEfGhI

アプリクライアントの作成
# アプリクライアントの作成
aws cognito-idp create-user-pool-client \
    --user-pool-id ap-northeast-1_AbCdEfGhI \
    --client-name MyAppClient \
    --generate-secret false \
    --explicit-auth-flows \
      ALLOW_USER_PASSWORD_AUTH \
      ALLOW_REFRESH_TOKEN_AUTH \
      ALLOW_USER_SRP_AUTH \
    --token-validity-units '{
      "AccessToken": "hours",
      "IdToken": "hours",
      "RefreshToken": "days"
    }' \
    --access-token-validity 1 \
    --id-token-validity 1 \
    --refresh-token-validity 30 \
    --region ap-northeast-1

# 出力例:
# {
#   "UserPoolClient": {
#     "ClientId": "1a2b3c4d5e6f7g8h9i0j1k2l3m",
#     "ClientName": "MyAppClient",
#     ...
#   }
# }


パラメータの説明

  • --client-name
    アプリクライアントの名前
  • --generate-secret
    クライアントシークレットを生成するかどうか
    false: パブリッククライアント (SPA、モバイルアプリ)
    true: コンフィデンシャルクライアント (サーバーサイドアプリ)
  • --explicit-auth-flows
    許可する認証フローのリスト
    ALLOW_USER_PASSWORD_AUTH: 直接的なユーザ名・パスワード認証
    ALLOW_USER_SRP_AUTH: SRPプロトコル認証 (推奨)
    ALLOW_REFRESH_TOKEN_AUTH: リフレッシュトークン認証
    ALLOW_CUSTOM_AUTH: カスタム認証フロー
  • --token-validity-units
    トークンの有効期限の単位
  • --access-token-validity
    アクセストークンの有効期限 (例: 1時間)
  • --id-token-validity
    IDトークンの有効期限 (例: 1時間)
  • --refresh-token-validity
    リフレッシュトークンの有効期限 (例: 30日)


※重要
Client IDを控えておく。(後の手順で使用)
例: 1a2b3c4d5e6f7g8h9i0j1k2l3m

ユーザプールドメインの設定 (オプション)

Hosted UIを使用する場合は、ドメインを設定する。

# ドメインの作成
aws cognito-idp create-user-pool-domain \
    --domain my-app-unique-domain-12345 \
    --user-pool-id ap-northeast-1_AbCdEfGhI \
    --region ap-northeast-1


※注意
ドメイン名はグローバルで一意である必要がある。
既に使用されているドメイン名は使用できない。

マネジメントコンソールでの作成

ユーザプールの作成
  1. Amazon Cognitoコンソールを開く。
  2. [ユーザプールを作成]を選択する。
  3. サインインエクスペリエンスを設定する。
    • プロバイダータイプ
      • Cognitoユーザプール
    • Cognitoユーザプールのサインインオプション
      • ユーザ名
      • Eメール
      • 電話番号
    [次へ]を選択する。
  4. セキュリティ要件を設定する。
    • パスワードポリシー
      • パスワードポリシーモードを選択する
        • Cognitoのデフォルト
        • カスタム
      • 最小文字数を入力する (8文字以上を推奨)
      • 文字要件を選択する
        • 大文字を含む
        • 小文字を含む
        • 数字を含む
        • 特殊文字を含む
    • 多要素認証 (MFA)
      • MFAなし (後で有効化可能)
      • MFAオプション (ユーザが選択)
      • MFA必須
    [次へ]を選択する。
  5. サインアップエクスペリエンスを設定する。
    • セルフサービスのサインアップを有効化する
    • 属性検証とユーザアカウントの確認
      • Cognitoがメッセージを自動的に送信することを許可する
      • メールで確認コードを送信する
    • 必須属性
      • emailを選択する (推奨)
    • カスタム属性 (オプション)
      • 必要に応じて追加する
    [次へ]を選択する。
  6. メッセージ配信を設定する。
    • Eメール
      • CognitoでEメールを送信する。(推奨、無料枠内で使用可能)
      • 送信元メールアドレス (オプション)
        • no-reply@verificationemail.com (デフォルト)
    • SMSメッセージ (オプション、MFA有効時に必要)
    [次へ]を選択する。
  7. アプリケーションを統合する。
    • ユーザプール名
      • 例: MyAppUserPool
    • Hosted authentication pages (オプション)
      • 必要に応じて有効化する
    • ドメイン (Hosted UI使用時)
      • Cognitoドメインを使用する
      • ドメインプレフィックスを入力する
    • 初期アプリクライアント
      • アプリタイプ
        • パブリッククライアント (SPA、モバイル)
        • 機密クライアント (サーバサイド)
      • アプリクライアント名
        • 例: MyAppClient
      • クライアントのシークレット
        • クライアントシークレットを生成しない (パブリッククライアント)
        • クライアントシークレットを生成する (機密クライアント)
    [次へ]を選択する。
  8. 設定内容を確認する。
  9. [ユーザプールを作成]を選択する。


作成完了後、以下の情報を控えておく。

  • ユーザプールID
    例: ap-northeast-1_AbCdEfGhI
  • アプリクライアントID
    例: 1a2b3c4d5e6f7g8h9i0j1k2l3m



API GatewayでのCognito認証設定 (REST API)

REST APIでCognito認証を設定する手順を示す。

REST APIの作成

# REST APIの作成
aws apigateway create-rest-api \
    --name MySecureAPI \
    --description "API with Cognito JWT authentication" \
    --endpoint-configuration types=REGIONAL \
    --region ap-northeast-1

# 出力からapi-idを記録
# 例: a1b2c3d4e5


Cognitoオーソライザーの作成

# Cognitoオーソライザーの作成
aws apigateway create-authorizer \
    --rest-api-id a1b2c3d4e5 \
    --name CognitoAuthorizer \
    --type COGNITO_USER_POOLS \
    --provider-arns arn:aws:cognito-idp:ap-northeast-1:123456789012:userpool/ap-northeast-1_AbCdEfGhI \
    --identity-source method.request.header.Authorization \
    --region ap-northeast-1

# 出力からauthorizer-idを記録
# 例: abc123


パラメータの説明

  • --type
    COGNITO_USER_POOLS を指定する
  • --provider-arns
    CognitoユーザプールのARN
    形式: arn:aws:cognito-idp:REGION:ACCOUNT_ID:userpool/USER_POOL_ID
    複数のユーザプールを指定可能 (カンマ区切り)
  • --identity-source
    トークンを取得するHTTPヘッダ
    method.request.header.Authorization を指定


リソースとメソッドの作成

# ルートリソースのIDを取得
ROOT_RESOURCE_ID=$(aws apigateway get-resources \
    --rest-api-id a1b2c3d4e5 \
    --region ap-northeast-1 \
    --query 'items[?path==`/`].id' \
    --output text)

# /usersリソースの作成
aws apigateway create-resource \
    --rest-api-id a1b2c3d4e5 \
    --parent-id $ROOT_RESOURCE_ID \
    --path-part users \
    --region ap-northeast-1

# 出力からresource-idを記録
# 例: xyz789


# GETメソッドの作成とCognitoオーソライザーの関連付け
aws apigateway put-method \
    --rest-api-id a1b2c3d4e5 \
    --resource-id xyz789 \
    --http-method GET \
    --authorization-type COGNITO_USER_POOLS \
    --authorizer-id abc123 \
    --request-parameters '{"method.request.header.Authorization":true}' \
    --region ap-northeast-1


パラメータの説明

  • --authorization-type
    COGNITO_USER_POOLS を指定
  • --authorizer-id
    先ほど作成したオーソライザーのID
  • --request-parameters
    Authorizationヘッダを必須にする


Lambda統合の設定

# Lambda統合の設定
aws apigateway put-integration \
    --rest-api-id a1b2c3d4e5 \
    --resource-id xyz789 \
    --http-method GET \
    --type AWS_PROXY \
    --integration-http-method POST \
    --uri arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:123456789012:function:MyFunction/invocations \
    --region ap-northeast-1

# Lambda関数にAPI Gatewayからの実行権限を付与
aws lambda add-permission \
    --function-name MyFunction \
    --statement-id apigateway-access \
    --action lambda:InvokeFunction \
    --principal apigateway.amazonaws.com \
    --source-arn "arn:aws:execute-api:ap-northeast-1:123456789012:a1b2c3d4e5/*" \
    --region ap-northeast-1


APIのデプロイ

# ステージへのデプロイ
aws apigateway create-deployment \
    --rest-api-id a1b2c3d4e5 \
    --stage-name prod \
    --stage-description "Production stage" \
    --description "Initial deployment" \
    --region ap-northeast-1



マネジメントコンソールでの設定

オーソライザーの作成
  1. API Gatewayコンソールで対象のREST APIを選択する。
  2. 左メニューから [オーソライザー]を選択する。
  3. [新しいオーソライザーの作成]を選択する。
  4. オーソライザーの設定を行う。
    • 名前
      • 例: CognitoAuthorizer
    • タイプ
      • Cognitoを選択
    • Cognitoユーザプール
      • 作成したユーザプールを選択
    • トークンのソース
      • Authorization (ヘッダ名)
    • トークンの検証
      • 省略可 (正規表現)
  5. [作成]を選択する。
  6. [テスト]を選択してオーソライザーをテストできる。
    • Authorizationヘッダに有効なIDトークンを入力
    • [テスト]を選択
    • 成功すると、ユーザ情報が表示される


メソッドへのオーソライザー適用
  1. 対象のリソースとメソッドを選択する。
  2. [メソッドリクエスト]を選択する。
  3. [編集]を選択する。
  4. 認証設定を変更する。
    • 認証
      • 作成したCognitoオーソライザーを選択
    • OAuthスコープ (オプション)
      • 必要に応じて指定
  5. [保存]を選択する。
  6. APIを再デプロイする。



API GatewayでのCognito認証設定 (HTTP API)

HTTP APIでCognito認証を設定する手順を示す。
REST APIよりもシンプルで低コストである。

HTTP APIの作成

# HTTP APIの作成
aws apigatewayv2 create-api \
    --name MySecureHttpAPI \
    --protocol-type HTTP \
    --description "HTTP API with Cognito JWT authentication" \
    --region ap-northeast-1

# 出力からapi-idを記録
# 例: abc123xyz


※重要
HTTP APIではapigatewayv2コマンドを使用する。
REST APIのapigatewayコマンドとは異なる。

JWT Authorizerの作成

# JWT Authorizerの作成
aws apigatewayv2 create-authorizer \
    --api-id abc123xyz \
    --name CognitoJWTAuthorizer \
    --authorizer-type JWT \
    --identity-source '$request.header.Authorization' \
    --jwt-configuration Audience=1a2b3c4d5e6f7g8h9i0j1k2l3m,Issuer=https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_AbCdEfGhI \
    --region ap-northeast-1

# 出力からauthorizer-idを記録
# 例: def456


パラメータの説明

  • --authorizer-type
    JWT を指定
  • --identity-source
    $request.header.Authorization を指定
    REST APIのmethod.request.header.Authorizationとは構文が異なる
  • --jwt-configuration
    Audience: CognitoのクライアントID
    Issuer: CognitoユーザプールのURL
    形式: https://cognito-idp.REGION.amazonaws.com/USER_POOL_ID


Lambda統合の作成

# Lambda統合の作成
aws apigatewayv2 create-integration \
    --api-id abc123xyz \
    --integration-type AWS_PROXY \
    --integration-uri arn:aws:lambda:ap-northeast-1:123456789012:function:MyFunction \
    --payload-format-version 2.0 \
    --region ap-northeast-1

# 出力からintegration-idを記録
# 例: ghi789


パラメータの説明

  • --payload-format-version
    2.0 を指定 (HTTP API推奨形式)
    Lambda関数がより詳細なリクエスト情報を受け取れる
    Cognitoから取得したユーザ情報もLambdaに渡される


ルートの作成とAuthorizerの関連付け

# ルートの作成
aws apigatewayv2 create-route \
    --api-id abc123xyz \
    --route-key 'GET /users' \
    --authorization-type JWT \
    --authorizer-id def456 \
    --target integrations/ghi789 \
    --region ap-northeast-1


パラメータの説明

  • --route-key
    HTTPメソッドとパスを指定
    例: 'GET /users', 'POST /items', 'PUT /items/{id}'
  • --authorization-type
    JWT を指定
  • --authorizer-id
    先ほど作成したJWT AuthorizerのID
  • --target
    統合ターゲットを指定
    形式: integrations/INTEGRATION_ID


ステージの作成とデプロイ

# デフォルトステージの作成 (自動デプロイ有効)
aws apigatewayv2 create-stage \
    --api-id abc123xyz \
    --stage-name '$default' \
    --auto-deploy \
    --region ap-northeast-1



※注意
$defaultステージを使用すると、URLにステージ名が含まれない。
REST APIのようなURLパターン (/prod/users) にはならない。

CORS設定 (Webブラウザからのアクセス時)

# CORS設定
aws apigatewayv2 update-api \
    --api-id abc123xyz \
    --cors-configuration AllowOrigins="https://example.com",AllowMethods="GET,POST,PUT,DELETE",AllowHeaders="Content-Type,Authorization",MaxAge=3600 \
    --region ap-northeast-1


※重要
本番環境ではAllowOriginsに具体的なドメインを指定する。
ワイルドカード (*) は開発時のみ使用する。

マネジメントコンソールでの設定

JWT Authorizerの作成
  1. API Gatewayコンソールで対象のHTTP APIを選択する。
  2. 左メニューから [承認] を選択する。
  3. [オーソライザーを作成] を選択する。
  4. オーソライザーの設定を行う。
  5. [作成] を選択する。


ルートへのオーソライザー適用
  1. 左メニューから [ルート] を選択する。
  2. 対象のルート (例: GET /users) を選択する。
  3. [承認をアタッチ] を選択する。
  4. 作成したJWT Authorizerを選択する。
  5. [保存] を選択する。



テストユーザの作成

動作確認用のテストユーザを作成する。

AWS CLIでのユーザ作成

管理者によるユーザ作成
# ユーザの作成 (管理者として)
aws cognito-idp admin-create-user \
    --user-pool-id ap-northeast-1_AbCdEfGhI \
    --username testuser@example.com \
    --user-attributes Name=email,Value=testuser@example.com Name=email_verified,Value=true \
    --temporary-password TempPass123! \
    --message-action SUPPRESS \
    --region ap-northeast-1


パラメータの説明

  • --username
    ユーザ名 (メールアドレス)
  • --user-attributes
    ユーザ属性
    email: メールアドレス
    email_verified: メール確認済みフラグ
  • --temporary-password
    一時パスワード (初回ログイン時に変更が必要)
  • --message-action
    SUPPRESS を指定すると確認メールを送信しない


永続パスワードの設定
# 永続パスワードの設定
aws cognito-idp admin-set-user-password \
    --user-pool-id ap-northeast-1_AbCdEfGhI \
    --username testuser@example.com \
    --password SecurePass123! \
    --permanent \
    --region ap-northeast-1


ユーザ自身によるサインアップ

# ユーザサインアップ
aws cognito-idp sign-up \
    --client-id 1a2b3c4d5e6f7g8h9i0j1k2l3m \
    --username testuser@example.com \
    --password SecurePass123! \
    --user-attributes Name=email,Value=testuser@example.com \
    --region ap-northeast-1

# メールで確認コードが送信される
# 確認コードでサインアップを確認
aws cognito-idp confirm-sign-up \
    --client-id 1a2b3c4d5e6f7g8h9i0j1k2l3m \
    --username testuser@example.com \
    --confirmation-code 123456 \
    --region ap-northeast-1


ユーザの認証

# 認証してJWTトークンを取得
aws cognito-idp admin-initiate-auth \
    --user-pool-id ap-northeast-1_AbCdEfGhI \
    --client-id 1a2b3c4d5e6f7g8h9i0j1k2l3m \
    --auth-flow ADMIN_NO_SRP_AUTH \
    --auth-parameters USERNAME=testuser@example.com,PASSWORD=SecurePass123! \
    --region ap-northeast-1

# 出力例:
# {
#   "AuthenticationResult": {
#     "AccessToken": "eyJraWQiOiI...",
#     "ExpiresIn": 3600,
#     "IdToken": "eyJraWQiOiI...",
#     "RefreshToken": "eyJjdHk...",
#     "TokenType": "Bearer"
#   }
# }


※重要
IdTokenを控えておく。API呼び出しに使用する。


API呼び出しのテスト

取得したJWTトークンを使用してAPIを呼び出す。

curlでのテスト

# トークンを環境変数に設定
ID_TOKEN="eyJraWQiOiI..."

# API Gatewayにリクエスト
curl -X GET \
    https://a1b2c3d4e5.execute-api.ap-northeast-1.amazonaws.com/prod/users \
    -H "Authorization: Bearer $ID_TOKEN" \
    -H "Content-Type: application/json"

# 成功すると、Lambda関数のレスポンスが返る
# 失敗すると、401 Unauthorizedが返る


jqを使用したトークン抽出

# 認証してトークンを取得し、IDトークンのみ抽出
ID_TOKEN=$(aws cognito-idp admin-initiate-auth \
    --user-pool-id ap-northeast-1_AbCdEfGhI \
    --client-id 1a2b3c4d5e6f7g8h9i0j1k2l3m \
    --auth-flow ADMIN_NO_SRP_AUTH \
    --auth-parameters USERNAME=testuser@example.com,PASSWORD=SecurePass123! \
    --region ap-northeast-1 \
    | jq -r '.AuthenticationResult.IdToken')

# APIを呼び出し
curl -X GET \
    https://a1b2c3d4e5.execute-api.ap-northeast-1.amazonaws.com/prod/users \
    -H "Authorization: Bearer $ID_TOKEN" \
    -H "Content-Type: application/json"


POSTリクエストの例

# POSTリクエスト
curl -X POST \
    https://a1b2c3d4e5.execute-api.ap-northeast-1.amazonaws.com/prod/users \
    -H "Authorization: Bearer $ID_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "山田太郎",
      "email": "yamada@example.com",
      "age": 30
    }'



認証フロー

CognitoとAPI Gatewayを使用した認証の流れを以下に示す。

新規ユーザ登録フロー

  1. ユーザがアプリケーションでサインアップする。
    • ユーザ名 (メールアドレス)、パスワード、メールアドレスを入力
  2. アプリケーションがCognito SignUp APIを呼び出す。
  3. Cognitoがパスワードポリシーを検証する。
    • 最小文字数、文字種別 (大文字、小文字、数字、記号) をチェック
  4. パスワードが要件を満たす場合、Cognitoがユーザアカウントを作成する。
    • ステータス: UNCONFIRMED
  5. Cognitoが確認コードをメールで送信する。
  6. ユーザが確認コードを入力する。
  7. アプリケーションがCognito ConfirmSignUp APIを呼び出す。
  8. Cognitoが確認コードを検証する。
    • 有効期限内 (24時間以内) かチェック
    • コードの一致を確認
  9. 確認成功後、ユーザステータスがCONFIRMEDに更新される。
  10. ユーザがログイン可能になる。


ログインとAPI呼び出しフロー

  1. ユーザがアプリケーションでログインする。
    • ユーザ名とパスワードを入力
  2. アプリケーションがCognito InitiateAuth APIを呼び出す
    • AuthFlow: USER_PASSWORD_AUTH または USER_SRP_AUTH
  3. Cognitoが認証情報を検証する。
    • ユーザの存在確認
    • パスワードのハッシュ照合 (bcrypt)
    • アカウントステータス確認
  4. 認証成功時、CognitoがJWTトークンを生成する。
    • IDトークン: ユーザ情報を含むJWT
    • アクセストークン: API呼び出し用JWT
    • リフレッシュトークン: トークン更新用
  5. アプリケーションがトークンを受け取り、メモリに保存する。
  6. ユーザがAPI呼び出しを要求する。
  7. アプリケーションがトークンの有効期限をチェックする。
    • 期限切れの場合はリフレッシュ処理へ
  8. アプリケーションがAPI GatewayにリクエストをHTTPS送信する。
    • ヘッダ: Authorization: Bearer <IDトークン>
  9. API Gatewayがリクエストを受信する。
  10. API GatewayがJWT Authorizerを実行する。
    • Authorizationヘッダからトークンを抽出
    • "Bearer "プレフィックスを除去
  11. API GatewayがCognitoの公開鍵を取得する。(初回またはキャッシュ期限切れ時)
  12. API GatewayがJWTトークンを検証する。
    • トークン構造検証 (ヘッダ、ペイロード、署名の3パート)
    • ヘッダのkidで公開鍵を選択
    • 署名検証 (RSA公開鍵を使用)
    • Issuer検証 (Cognitoユーザプール)
    • Audience検証 (クライアントID)
    • 有効期限検証 (expクレーム)
    • トークン発行時刻検証 (iatクレーム)
  13. JWT検証が失敗した場合
    • API Gatewayが401 Unauthorizedを返す
    • アプリケーションがエラーメッセージを表示する
  14. JWT検証が成功した場合
    • API Gatewayがコンテキストを構築する
    • ユーザ情報 (sub、email等) をコンテキストに含める
    • Lambda関数を呼び出す
  15. Lambda関数がリクエストを処理する
    • event.requestContext.authorizer.jwt.claimsからユーザ情報を取得
    • ユーザIDでデータベースをクエリ
    • ビジネスロジックを実行
  16. Lambda関数がレスポンスを返す。
  17. API Gatewayがレスポンスをクライアントに転送する。
  18. アプリケーションがレスポンスを処理してユーザに表示する。


トークンリフレッシュフロー

  1. トークンの有効期限が切れそうになる。(例: 残り5分)
  2. アプリケーションがトークンリフレッシュを判断する。
  3. アプリケーションがCognito InitiateAuth APIを呼び出す。
    • AuthFlow: REFRESH_TOKEN_AUTH
    • AuthParameters: REFRESH_TOKEN
  4. Cognitoがリフレッシュトークンを検証する。
    • トークン形式確認
    • トークンの有効性確認
    • 無効化リスト (ブラックリスト) をチェック
    • 有効期限確認 (デフォルト30日)
  5. リフレッシュトークンが有効な場合
    • Cognitoが新しいIDトークンとアクセストークンを生成する。
    • リフレッシュトークン自体は再利用される。(返却されない)
  6. リフレッシュトークンが無効な場合
    • Cognitoがエラーを返す。(NotAuthorizedException)
    • アプリケーションがトークンをクリアする。
    • ユーザに再ログインを要求する。
  7. アプリケーションが新しいトークンを受け取る。
  8. アプリケーションがトークンを更新する。
  9. アプリケーションが新しいトークンでAPIを呼び出す。


MFA有効時のログインフロー

  1. ユーザがユーザ名とパスワードでログインを試みる。
  2. アプリケーションがCognito InitiateAuth APIを呼び出す。
  3. Cognitoがパスワードを検証する。(第1段階認証)
  4. パスワードが正しい場合、CognitoがMFAチャレンジを返す。
    • ChallengeName: SOFTWARE_TOKEN_MFA
    • Session: 一時セッショントークン
  5. アプリケーションがMFAコード入力画面を表示する。
  6. ユーザが認証アプリ (Google Authenticator等) を開く。
  7. 認証アプリがTOTPアルゴリズムで6桁のコードを生成する。
    • 30秒ごとに更新される。
  8. ユーザがコードを入力する。
  9. アプリケーションがCognito RespondToAuthChallenge APIを呼び出す。
    • ChallengeName: SOFTWARE_TOKEN_MFA
    • ChallengeResponses: SOFTWARE_TOKEN_MFA_CODE
    • Session: 前回受け取ったセッショントークン
  10. Cognitoがサーバー側でTOTPを計算してコードを検証する。
    • ±1タイムステップの範囲で照合 (時計のずれを許容)
    • コード再利用防止チェック
  11. コードが有効な場合
    • Cognitoが認証完了を記録する。
    • JWTトークンを生成して返す。
  12. コードが無効な場合
    • Cognitoがエラーを返す。(CodeMismatchException)
    • ユーザは新しいコードで再試行可能 (セッション有効時)



Lambda関数でのユーザ情報取得

API GatewayからLambda関数に渡されるユーザ情報の取得方法を示す。

REST APIの場合

REST APIでは、ユーザ情報はevent.requestContext.authorizerに含まれる。

 # lambda_function.py (REST API用)
 
 import json
 
 def lambda_handler(event, context):
    # REST APIからのユーザ情報取得
    authorizer = event.get('requestContext', {}).get('authorizer', {})
 
    if authorizer:
       # Cognitoのユーザ識別子
       user_id = authorizer.get('claims', {}).get('sub')
 
       # メールアドレス
       email = authorizer.get('claims', {}).get('email')
 
       # ユーザ名
       username = authorizer.get('claims', {}).get('cognito:username')
 
       print(f"User: {username} ({email}) - ID: {user_id}")
 
    return {
       'statusCode': 200,
       'headers': {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*'
       },
       'body': json.dumps({
          'message': 'Success',
          'userId': user_id,
          'email': email
       })
    }


HTTP APIの場合

HTTP APIでは、ユーザ情報はevent.requestContext.authorizer.jwt.claimsに含まれる。

 # lambda_function.py (HTTP API用)
 
 import json
 
 def lambda_handler(event, context):
    # HTTP APIからのユーザ情報取得
    claims = event.get('requestContext', {}).get('authorizer', {}).get('jwt', {}).get('claims', {})
 
    if claims:
       # Cognitoのユーザ識別子
       user_id = claims.get('sub')
 
       # メールアドレス
       email = claims.get('email')
 
       # ユーザ名
       username = claims.get('cognito:username')
 
       print(f"User: {username} ({email}) - ID: {user_id}")
 
    # DynamoDBからユーザデータを取得する例
    import boto3
    dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table('Users')
 
    response = table.get_item(Key={'userId': user_id})
    user_data = response.get('Item', {})
 
    return {
       'statusCode': 200,
       'headers': {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*'
       },
       'body': json.dumps({
          'message': 'Success',
          'user': user_data
       })
    }


利用可能なクレーム

JWTトークンのペイロードに含まれる主なクレームを以下に示す。

  • sub
    ユーザの一意の識別子 (UUID形式)
    例: abc-123-def-456-ghi-789
  • email
    ユーザのメールアドレス
  • email_verified
    メールアドレスが確認済みかどうか (true/false)
  • cognito:username
    Cognitoのユーザ名
  • cognito:groups
    ユーザが属するグループのリスト
  • iss
    トークン発行者 (Issuer)
    例: https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_AbCdEfGhI
  • aud
    トークンの対象者 (Audience)
    CognitoのクライアントID
  • exp
    トークンの有効期限 (Unixタイムスタンプ)
  • iat
    トークンの発行時刻 (Unixタイムスタンプ)
  • token_use
    トークンの用途 (id または access)


カスタム属性を追加した場合、それらもクレームに含まれる。


セキュリティベストプラクティス

認証情報の管理

  • コードに直接埋め込まない
    パスワード、クライアントシークレット、APIキーをコードに埋め込まない。
    環境変数、AWS Secrets Manager、Parameter Storeを使用する。
  • 環境変数の使用例
     # 環境変数の設定
     export AWS_USER_POOL_ID="ap-northeast-1_AbCdEfGhI"
     export AWS_CLIENT_ID="1a2b3c4d5e6f7g8h9i0j1k2l3m"
     
     # アプリケーションから環境変数を読み込む
    

  • AWS Secrets Managerの使用例
     import boto3
     import json
     
     def get_secret(secret_name):
        client = boto3.client('secretsmanager', region_name='ap-northeast-1')
        response = client.get_secret_value(SecretId=secret_name)
        return json.loads(response['SecretString'])
     
     # シークレットの取得
     credentials = get_secret('prod/myapp/cognito')
     user_pool_id = credentials['userPoolId']
     client_id = credentials['clientId']
    


トークンの安全な保存

  • サーバサイド
    メモリまたはセッションストアに保存する。
    データベースに保存する場合は暗号化する。
  • クライアントサイド (ブラウザ)
    HttpOnly、Secure、SameSite属性を持つCookieに保存する。
    LocalStorageやSessionStorageは避ける。(XSS攻撃のリスク)
  • モバイルアプリ
    iOSではKeychain、AndroidではKeyStoreを使用する。
    平文でファイルに保存しない。


HTTPS通信の強制

  • 本番環境では必ずHTTPSを使用する。
    HTTP通信ではトークンが平文で送信されるため。
  • API Gatewayのカスタムドメイン
    ACM (AWS Certificate Manager) でSSL/TLS証明書を取得する。
    カスタムドメインを設定する。
  • アプリケーションでの検証。
    SSL/TLS証明書の検証を必ず有効にする。
    自己署名証明書は本番環境で使用しない。


IAMポリシーの最小権限設定

Lambda実行ロールの最小権限ポリシー例を以下に示す。

 {
   "Version": "2012-10-17",
   "Statement": [
     {
       "Effect": "Allow",
       "Action": [
         "logs:CreateLogGroup",
         "logs:CreateLogStream",
         "logs:PutLogEvents"
       ],
       "Resource": "arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/lambda/MyFunction:*"
     },
     {
       "Effect": "Allow",
       "Action": [
         "dynamodb:GetItem",
         "dynamodb:PutItem",
         "dynamodb:UpdateItem",
         "dynamodb:Query"
       ],
       "Resource": "arn:aws:dynamodb:ap-northeast-1:123456789012:table/MyTable",
       "Condition": {
         "ForAllValues:StringEquals": {
           "dynamodb:LeadingKeys": ["${cognito:sub}"]
         }
       }
     }
   ]
 }


条件付きアクセスの説明

  • dynamodb:LeadingKeys
    DynamoDBのパーティションキーを制限する
    ${cognito:sub}はCognitoのユーザID
    ユーザは自分のデータのみアクセス可能


パスワードポリシーの強化

  • 最小文字数
    12文字以上を推奨 (デフォルトは8文字)
  • 文字種別
    大文字、小文字、数字、記号を全て必須にする
  • 一時パスワードの有効期限
    短く設定する (例: 7日)
  • パスワード履歴
    過去のパスワードの再利用を防ぐ


MFAの有効化

  • セキュリティ要件が高い場合はMFAを必須にする
    オンラインバンキング、医療情報、企業システム等
  • TOTP (Time-based One-Time Password) を使用する
    Google Authenticator、Microsoft Authenticator、Authy等
  • SMSよりもTOTPを推奨する
    SMSはSIMスワップ攻撃のリスクがある


トークンの有効期限設定

  • IDトークンとアクセストークン
    短く設定する (15分〜1時間)
    セキュリティとユーザ体験のバランスを考慮する
  • リフレッシュトークン
    適度な期間を設定する (7日〜30日)
    長すぎるとセキュリティリスクが増加する
    短すぎるとユーザが頻繁にログインする必要がある


ログとモニタリング

  • CloudWatch Logsの有効化
    API Gatewayのアクセスログを記録する
    Lambda関数のログを記録する
  • 異常なアクティビティの検出
    認証失敗の監視
    異常なアクセスパターンの検出
    CloudWatch Alarmsで通知を設定する
  • AWS CloudTrailの有効化
    API呼び出しの監査ログを記録する
    セキュリティインシデント時の調査に使用する



コストと制限事項

Cognitoの料金

無料枠
  • MAU (月間アクティブユーザ) 50,000人まで無料
    MAUは1ヶ月間に1回以上認証したユーザ数
    小規模アプリケーションは完全無料で運用可能


従量課金料金 (東京リージョン)
  • 50,001~100,000 MAU
    $0.0055/MAU
  • 100,001~1,000,000 MAU
    $0.0046/MAU
  • 1,000,001~10,000,000 MAU
    $0.00325/MAU
  • 10,000,001 MAU以上
    カスタム価格


料金計算例

  • 月間10,000 MAUの場合
    無料枠内
    コスト : $0
  • 月間100,000 MAUの場合
    50,000人まで : $0
    50,001~100,000人 : 50,000 × $0.0055 = $275
    合計: $275/月
  • 月間500,000 MAUの場合
    50,000人まで : $0
    50,001〜100,000人 : 50,000 × $0.0055 = $275
    100,001〜500,000人 : 400,000 × $0.0046 = $1,840
    合計: $2,115/月


コスト最適化のヒント
  • トークンの有効期限を適切に設定する
    短すぎるとリフレッシュ頻度が増える
    ただし、セキュリティとのバランスを考慮する
  • 不要なユーザ属性を追加しない
    カスタム属性の数はコストに影響しない
    ただし、トークンサイズが増加する
  • MFAは必要な場合のみ有効化する
    SMS MFAは追加料金が発生する (SNS経由)
    TOTP MFAは追加料金なし


API Gatewayの料金

HTTP APIとREST APIの料金比較 (東京リージョン)

HTTP API
  • APIコール
    月100万回まで無料 (永続的)
    $1.17/100万リクエスト
  • データ転送
    送信 (OUT) : $0.114/GB (最初の10TB)
    受信 (IN) : 無料


REST API
  • APIコール
    月100万回まで無料 (最初の12ヶ月のみ)
    $4.25/100万リクエスト
  • データ転送
    送信 (OUT) : $0.114/GB (最初の10TB)
    受信 (IN) : 無料
  • キャッシング (オプション)
    0.5[GB] : $0.020/時間
    1.6[GB] : $0.038/時間
    6.1[GB] : $0.20/時間


コスト比較例

月間1,000万リクエストの場合

  • HTTP API
    (10,000,000 - 1,000,000) × $1.17 / 1,000,000 = $10.53/月
  • REST API (12ヶ月後)
    (10,000,000 - 1,000,000) × $4.25 / 1,000,000 = $38.25/月
  • 差額
    $27.72/月、年間$332.64の節約


※重要
HTTP APIはREST APIの約3分の1のコストである。
新規プロジェクトではHTTP APIを優先的に検討する。

Cognitoの制限事項

  • ユーザプールあたり最大40,000,000ユーザ
  • トークン生成レート
    120リクエスト/秒 (バーストあり)
  • カスタム属性
    最大50個
  • IDトークン最大サイズ
    8[KB]
  • ユーザ名最大長
    128文字
  • パスワード最大長
    256文字


API Gatewayの制限事項

  • タイムアウト
    統合タイムアウト: 29秒 (最大値)
  • ペイロードサイズ
    リクエスト: 10[MB]
    レスポンス: 10[MB]
  • スロットリング制限
    デフォルト: 10,000リクエスト/秒
    バースト: 25,000リクエスト/秒
    リージョンごとの上限 (上限緩和申請可能)
  • APIステージ
    10個/API
  • ルート数 (HTTP API)
    300個/API


対策

  • Cognitoのレート制限対策
    キャッシュ層を導入する
    トークンの有効期限を長めに設定する
    AWS Supportに上限緩和を申請する
  • API Gatewayタイムアウト対策
    長時間処理は非同期化する (SQS + Lambda)
    ステップ関数 (Step Functions) を使用する
  • 大きなファイルの処理
    S3署名付きURLを使用する
    直接S3にアップロード/ダウンロードする



トラブルシューティング

401 Unauthorized エラー

API Gatewayへのリクエストで401エラーが返る場合の原因と対策を示す。

原因1: トークンの有効期限切れ
  • 症状
    リクエストを送信すると401エラーが返る
    エラーメッセージ: "The incoming token has expired"
  • 確認方法
    • トークンのペイロードをデコードして確認 (jwt.io等)
    • expクレームと現在時刻を比較

  • 対策
    リフレッシュトークンを使用して新しいトークンを取得する
    アプリケーションに自動リフレッシュ機能を実装する


原因2: Authorizationヘッダの形式が間違っている
  • 正しい形式
    Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
  • 間違った形式
     # "Bearer "が抜けている
     Authorization: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
     
     # ヘッダ名が間違っている
     Auth: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
    
  • 対策
    ヘッダ名を"Authorization"にする
    "Bearer "プレフィックスを追加する (スペースに注意)


原因3: IDトークンとアクセストークンの混同
  • 症状
    アクセストークンを送信しているが401エラーが返る
  • 原因
    API GatewayのCognitoオーソライザーにはIDトークンを使用する必要がある
  • 対策
    IDトークンを送信する
     # 正しい例
     headers = {
         'Authorization': f'Bearer {id_token}'  # IDトークンを使用
     }
     
     # 間違った例
     headers = {
         'Authorization': f'Bearer {access_token}'  # アクセストークン
     }
    


原因4: オーソライザーの設定ミス
  • 確認事項
    Issuer URLが正しいか
    AudienceにクライアントIDが設定されているか
    Identity Sourceが正しいか (REST API: method.request.header.Authorization、HTTP API: $request.header.Authorization)
  • 確認方法
     # REST API
     aws apigateway get-authorizers \
         --rest-api-id a1b2c3d4e5 \
         --region ap-northeast-1
     
     # HTTP API
     aws apigatewayv2 get-authorizers \
         --api-id abc123xyz \
         --region ap-northeast-1
    

  • 対策
    オーソライザーの設定を修正する
    APIを再デプロイする


CORSエラー

WebブラウザからAPIを呼び出す場合のCORSエラーの対策を示す。

症状
  • ブラウザのコンソールにエラーが表示される
    Access to fetch at 'https://...' has been blocked by CORS policy


REST APIでの対策
  1. リソースを選択する
  2. [アクション] - [CORSの有効化]を選択する。
  3. 必要なオリジン、メソッド、ヘッダを設定する。
  4. [CORSを有効にして既存のCORSヘッダを置換]を選択する。
  5. APIを再デプロイする。


HTTP APIでの対策
# CORS設定
aws apigatewayv2 update-api \
    --api-id abc123xyz \
    --cors-configuration \
      AllowOrigins="https://example.com" \
      AllowMethods="GET,POST,PUT,DELETE,OPTIONS" \
      AllowHeaders="Content-Type,Authorization" \
      MaxAge=3600 \
    --region ap-northeast-1


Lambda関数での対策

Lambda関数のレスポンスにCORSヘッダを追加する。

 def lambda_handler(event, context):
    return {
       'statusCode': 200,
       'headers': {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*',  # 本番環境では具体的なドメインを指定
          'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS',
          'Access-Control-Allow-Headers': 'Content-Type,Authorization'
       },
       'body': json.dumps({'message': 'Success'})
    }


※重要
本番環境ではAccess-Control-Allow-Originに具体的なドメインを指定する。
ワイルドカード (*) は開発時のみ使用する。

"User pool client does not exist" エラー

原因
  • ClientIdが間違っている
  • アプリクライアントが削除された
  • リージョンが間違っている


確認方法
# ユーザプールのクライアント一覧を取得
aws cognito-idp list-user-pool-clients \
    --user-pool-id ap-northeast-1_AbCdEfGhI \
    --region ap-northeast-1


対策
  • 正しいClientIdを使用する
  • リージョンを確認する
  • 必要に応じてアプリクライアントを再作成する


"Username or password is incorrect" エラー

原因
  • ユーザ名またはパスワードが間違っている
  • ユーザが存在しない
  • ユーザのステータスがCONFIRMEDではない


確認方法
# ユーザの状態を確認
aws cognito-idp admin-get-user \
    --user-pool-id ap-northeast-1_AbCdEfGhI \
    --username testuser@example.com \
    --region ap-northeast-1


対策
  • ユーザ名とパスワードを確認する
  • ユーザが確認済み (CONFIRMED) か確認する
  • 必要に応じてパスワードをリセットする


トークンのデコードとデバッグ

JWTトークンの内容を確認する方法を示す。

オンラインツール
  • jwt.io
    https://jwt.io/
    ブラウザでトークンをデコードできる
    署名の検証も可能


※注意
本番環境のトークンを外部サイトに入力しない。
開発環境のトークンのみ使用する。

コマンドラインでのデコード
# トークンを3つの部分に分割 (ヘッダ、ペイロード、署名)
TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI..."

# ペイロード部分を抽出してBase64デコード
echo $TOKEN | cut -d'.' -f2 | base64 -d | jq .

# 出力例
# {
#   "sub": "abc-123-def-456",
#   "email": "testuser@example.com",
#   "cognito:username": "testuser@example.com",
#   "exp": 1738159080,
#   "iat": 1738155480,
#   ...
# }


Pythonでのデコード
 import jwt
 import json
 
 token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
 
 # 署名検証なしでデコード (デバッグ用)
 decoded = jwt.decode(token, options={"verify_signature": False})
 print(json.dumps(decoded, indent=2))


ログの有効化

トラブルシューティングのため、CloudWatch Logsを有効化する。

REST APIのログ有効化
  1. ステージを選択する
  2. [ログ/トレース] タブを選択する
  3. CloudWatch設定を有効化する
    • [CloudWatch Logs:]にチェックを入力する。
    • ログレベル: INFO または ERROR
    • 完全なリクエスト/レスポンスデータのログ記録: 必要に応じてチェックする。
  4. IAMロールを設定する。
  5. [変更の保存] を選択する。
  6. APIを再デプロイする。


HTTP APIのログ有効化
  1. ステージを選択する。
  2. [ログとトレース]を選択する。
  3. アクセスログを有効化する。
  4. CloudWatch Logsのロググループを指定する。
    例: /aws/apigateway/my-http-api
  5. ログ形式を選択する。
    JSON形式を推奨
  6. [保存]を選択する。


CloudWatch Logsでのログ確認
# ロググループのログストリーム一覧を取得
aws logs describe-log-streams \
    --log-group-name /aws/apigateway/abc123xyz \
    --order-by LastEventTime \
    --descending \
    --region ap-northeast-1

# ログイベントを取得
aws logs get-log-events \
    --log-group-name /aws/apigateway/abc123xyz \
    --log-stream-name "2026/01/30/[$LATEST]abc123..." \
    --region ap-northeast-1