概要
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
※注意
ドメイン名はグローバルで一意である必要がある。
既に使用されているドメイン名は使用できない。
マネジメントコンソールでの作成
ユーザプールの作成
- Amazon Cognitoコンソールを開く。
- [ユーザプールを作成]を選択する。
- サインインエクスペリエンスを設定する。
- プロバイダータイプ
- Cognitoユーザプール
- Cognitoユーザプールのサインインオプション
- ユーザ名
- Eメール
- 電話番号
- [次へ]を選択する。
- プロバイダータイプ
- セキュリティ要件を設定する。
- パスワードポリシー
- パスワードポリシーモードを選択する
- Cognitoのデフォルト
- カスタム
- 最小文字数を入力する (8文字以上を推奨)
- 文字要件を選択する
- 大文字を含む
- 小文字を含む
- 数字を含む
- 特殊文字を含む
- パスワードポリシーモードを選択する
- 多要素認証 (MFA)
- MFAなし (後で有効化可能)
- MFAオプション (ユーザが選択)
- MFA必須
- [次へ]を選択する。
- パスワードポリシー
- サインアップエクスペリエンスを設定する。
- セルフサービスのサインアップを有効化する
- 属性検証とユーザアカウントの確認
- Cognitoがメッセージを自動的に送信することを許可する
- メールで確認コードを送信する
- 必須属性
- emailを選択する (推奨)
- カスタム属性 (オプション)
- 必要に応じて追加する
- [次へ]を選択する。
- メッセージ配信を設定する。
- Eメール
- CognitoでEメールを送信する。(推奨、無料枠内で使用可能)
- 送信元メールアドレス (オプション)
- no-reply@verificationemail.com (デフォルト)
- SMSメッセージ (オプション、MFA有効時に必要)
- [次へ]を選択する。
- Eメール
- アプリケーションを統合する。
- ユーザプール名
- 例: MyAppUserPool
- Hosted authentication pages (オプション)
- 必要に応じて有効化する
- ドメイン (Hosted UI使用時)
- Cognitoドメインを使用する
- ドメインプレフィックスを入力する
- 初期アプリクライアント
- アプリタイプ
- パブリッククライアント (SPA、モバイル)
- 機密クライアント (サーバサイド)
- アプリクライアント名
- 例: MyAppClient
- クライアントのシークレット
- クライアントシークレットを生成しない (パブリッククライアント)
- クライアントシークレットを生成する (機密クライアント)
- アプリタイプ
- [次へ]を選択する。
- ユーザプール名
- 設定内容を確認する。
- [ユーザプールを作成]を選択する。
作成完了後、以下の情報を控えておく。
- ユーザプール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
マネジメントコンソールでの設定
オーソライザーの作成
- API Gatewayコンソールで対象のREST APIを選択する。
- 左メニューから [オーソライザー]を選択する。
- [新しいオーソライザーの作成]を選択する。
- オーソライザーの設定を行う。
- 名前
- 例: CognitoAuthorizer
- タイプ
- Cognitoを選択
- Cognitoユーザプール
- 作成したユーザプールを選択
- トークンのソース
- Authorization (ヘッダ名)
- トークンの検証
- 省略可 (正規表現)
- 名前
- [作成]を選択する。
- [テスト]を選択してオーソライザーをテストできる。
- Authorizationヘッダに有効なIDトークンを入力
- [テスト]を選択
- 成功すると、ユーザ情報が表示される
メソッドへのオーソライザー適用
- 対象のリソースとメソッドを選択する。
- [メソッドリクエスト]を選択する。
- [編集]を選択する。
- 認証設定を変更する。
- 認証
- 作成したCognitoオーソライザーを選択
- OAuthスコープ (オプション)
- 必要に応じて指定
- 認証
- [保存]を選択する。
- 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
- APIエンドポイントURLの例
※注意
$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の作成
- API Gatewayコンソールで対象のHTTP APIを選択する。
- 左メニューから [承認] を選択する。
- [オーソライザーを作成] を選択する。
- オーソライザーの設定を行う。
- オーソライザータイプ
- JWT を選択
- 名前
- 例: CognitoJWTAuthorizer
- アイデンティティソース
- $request.header.Authorization
- 発行者URL
- 対象ユーザ
- CognitoアプリクライアントIDを入力
- 例: 1a2b3c4d5e6f7g8h9i0j1k2l3m
- オーソライザータイプ
- [作成] を選択する。
ルートへのオーソライザー適用
- 左メニューから [ルート] を選択する。
- 対象のルート (例: GET /users) を選択する。
- [承認をアタッチ] を選択する。
- 作成したJWT Authorizerを選択する。
- [保存] を選択する。
テストユーザの作成
動作確認用のテストユーザを作成する。
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を使用した認証の流れを以下に示す。
新規ユーザ登録フロー
- ユーザがアプリケーションでサインアップする。
- ユーザ名 (メールアドレス)、パスワード、メールアドレスを入力
- アプリケーションがCognito SignUp APIを呼び出す。
- Cognitoがパスワードポリシーを検証する。
- 最小文字数、文字種別 (大文字、小文字、数字、記号) をチェック
- パスワードが要件を満たす場合、Cognitoがユーザアカウントを作成する。
- ステータス: UNCONFIRMED
- Cognitoが確認コードをメールで送信する。
- ユーザが確認コードを入力する。
- アプリケーションがCognito ConfirmSignUp APIを呼び出す。
- Cognitoが確認コードを検証する。
- 有効期限内 (24時間以内) かチェック
- コードの一致を確認
- 確認成功後、ユーザステータスがCONFIRMEDに更新される。
- ユーザがログイン可能になる。
ログインとAPI呼び出しフロー
- ユーザがアプリケーションでログインする。
- ユーザ名とパスワードを入力
- アプリケーションがCognito InitiateAuth APIを呼び出す
- AuthFlow: USER_PASSWORD_AUTH または USER_SRP_AUTH
- Cognitoが認証情報を検証する。
- ユーザの存在確認
- パスワードのハッシュ照合 (bcrypt)
- アカウントステータス確認
- 認証成功時、CognitoがJWTトークンを生成する。
- IDトークン: ユーザ情報を含むJWT
- アクセストークン: API呼び出し用JWT
- リフレッシュトークン: トークン更新用
- アプリケーションがトークンを受け取り、メモリに保存する。
- ユーザがAPI呼び出しを要求する。
- アプリケーションがトークンの有効期限をチェックする。
- 期限切れの場合はリフレッシュ処理へ
- アプリケーションがAPI GatewayにリクエストをHTTPS送信する。
- ヘッダ: Authorization: Bearer <IDトークン>
- API Gatewayがリクエストを受信する。
- API GatewayがJWT Authorizerを実行する。
- Authorizationヘッダからトークンを抽出
- "Bearer "プレフィックスを除去
- API GatewayがCognitoの公開鍵を取得する。(初回またはキャッシュ期限切れ時)
- API GatewayがJWTトークンを検証する。
- トークン構造検証 (ヘッダ、ペイロード、署名の3パート)
- ヘッダのkidで公開鍵を選択
- 署名検証 (RSA公開鍵を使用)
- Issuer検証 (Cognitoユーザプール)
- Audience検証 (クライアントID)
- 有効期限検証 (expクレーム)
- トークン発行時刻検証 (iatクレーム)
- JWT検証が失敗した場合
- API Gatewayが401 Unauthorizedを返す
- アプリケーションがエラーメッセージを表示する
- JWT検証が成功した場合
- API Gatewayがコンテキストを構築する
- ユーザ情報 (sub、email等) をコンテキストに含める
- Lambda関数を呼び出す
- Lambda関数がリクエストを処理する
- event.requestContext.authorizer.jwt.claimsからユーザ情報を取得
- ユーザIDでデータベースをクエリ
- ビジネスロジックを実行
- Lambda関数がレスポンスを返す。
- API Gatewayがレスポンスをクライアントに転送する。
- アプリケーションがレスポンスを処理してユーザに表示する。
トークンリフレッシュフロー
- トークンの有効期限が切れそうになる。(例: 残り5分)
- アプリケーションがトークンリフレッシュを判断する。
- アプリケーションがCognito InitiateAuth APIを呼び出す。
- AuthFlow: REFRESH_TOKEN_AUTH
- AuthParameters: REFRESH_TOKEN
- Cognitoがリフレッシュトークンを検証する。
- トークン形式確認
- トークンの有効性確認
- 無効化リスト (ブラックリスト) をチェック
- 有効期限確認 (デフォルト30日)
- リフレッシュトークンが有効な場合
- Cognitoが新しいIDトークンとアクセストークンを生成する。
- リフレッシュトークン自体は再利用される。(返却されない)
- リフレッシュトークンが無効な場合
- Cognitoがエラーを返す。(NotAuthorizedException)
- アプリケーションがトークンをクリアする。
- ユーザに再ログインを要求する。
- アプリケーションが新しいトークンを受け取る。
- アプリケーションがトークンを更新する。
- アプリケーションが新しいトークンでAPIを呼び出す。
MFA有効時のログインフロー
- ユーザがユーザ名とパスワードでログインを試みる。
- アプリケーションがCognito InitiateAuth APIを呼び出す。
- Cognitoがパスワードを検証する。(第1段階認証)
- パスワードが正しい場合、CognitoがMFAチャレンジを返す。
- ChallengeName: SOFTWARE_TOKEN_MFA
- Session: 一時セッショントークン
- アプリケーションがMFAコード入力画面を表示する。
- ユーザが認証アプリ (Google Authenticator等) を開く。
- 認証アプリがTOTPアルゴリズムで6桁のコードを生成する。
- 30秒ごとに更新される。
- ユーザがコードを入力する。
- アプリケーションがCognito RespondToAuthChallenge APIを呼び出す。
- ChallengeName: SOFTWARE_TOKEN_MFA
- ChallengeResponses: SOFTWARE_TOKEN_MFA_CODE
- Session: 前回受け取ったセッショントークン
- Cognitoがサーバー側でTOTPを計算してコードを検証する。
- ±1タイムステップの範囲で照合 (時計のずれを許容)
- コード再利用防止チェック
- コードが有効な場合
- Cognitoが認証完了を記録する。
- JWTトークンを生成して返す。
- コードが無効な場合
- 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での対策
- リソースを選択する
- [アクション] - [CORSの有効化]を選択する。
- 必要なオリジン、メソッド、ヘッダを設定する。
- [CORSを有効にして既存のCORSヘッダを置換]を選択する。
- 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のログ有効化
- ステージを選択する
- [ログ/トレース] タブを選択する
- CloudWatch設定を有効化する
- [CloudWatch Logs:]にチェックを入力する。
- ログレベル: INFO または ERROR
- 完全なリクエスト/レスポンスデータのログ記録: 必要に応じてチェックする。
- IAMロールを設定する。
- [変更の保存] を選択する。
- APIを再デプロイする。
HTTP APIのログ有効化
- ステージを選択する。
- [ログとトレース]を選択する。
- アクセスログを有効化する。
- CloudWatch Logsのロググループを指定する。
- 例: /aws/apigateway/my-http-api
- ログ形式を選択する。
- JSON形式を推奨
- [保存]を選択する。
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