はじめに
「ログインしたユーザーだけが使えるAPIを作りたい」——そんな要件を、Amazon Cognito + API Gateway + Lambda はサーバー側の認証コードをほぼ書かずに実現します。
この記事では、AWSコンソールのみを使って、以下の構成の認証付き REST API をゼロから構築するハンズオンを紹介します。
クライアント(curl)
│
├─ GET /hello → 認証不要 → Lambda → 200 OK
│
└─ GET /profile
├─ Authorizationヘッダーなし → API Gateway が 401 を返す(Lambda は呼ばれない)
└─ Authorization: <IDトークン>
→ Cognito Authorizer が JWT を検証(Lambda 不要)
→ Lambda → email / sub を返す → 200 OKこのハンズオンで体験できること:
- Amazon Cognito User Pool によるユーザー管理と JWT 発行
- API Gateway の Cognito Authorizer を使ったエンドポイントごとの認証制御
- Lambda 側で認証コードを書かずに
claims(ユーザー情報)を取得するパターン - 新UI対応 — Cognito コンソールの大幅変更点と、操作でハマりやすいポイント
この記事は SAM版ハンズオン の比較記事です。
コンソール操作でCognitoとAPI Gatewayの連携を視覚的に学び、SAMと何が違うのかを比較したい方向けです。
Cognito 新UIで戸惑いやすいポイント(先読み)
操作を始める前に、2026年時点の新UIで戸惑いやすい変更点を把握しておきましょう。
| 変更点 | 旧UI | 新UI(現在) |
|---|---|---|
| User Pool 作成の入口 | 「ユーザープールを作成」ボタンから | **「アプリケーションを作成」**ボタンから(名称が変わった) |
| 作成の粒度 | User Pool → App Client を別々に作成 | 1ページのウィザードで User Pool とクライアントを同時作成 |
| クライアントシークレット | デフォルトなし | 「従来のウェブアプリケーション」を選ぶとシークレット必須になる |
| User Pool 名 | 指定した名前がそのまま付く | アプリ名の前に User pool - が自動で付く(例: User pool - MyWebApp) |
| クライアント ID の場所 | 概要ページに表示 | 左サイドバー → 「アプリケーション」から辿る |
最大のハマりポイント(クライアントシークレット):
新UIの「アプリケーションを作成」で「従来のウェブアプリケーション」を選ぶと、クライアントシークレットが自動で付与されます。シークレットありのクライアントでinitiate-authを実行するとSECRET_HASH was not receivedエラーが出て詰まります。
このハンズオンでは②の手順でシークレットなしのクライアントを別途作成します。
キーワード解説
| 用語 | 意味 |
|---|---|
| User Pool | Cognitoのユーザーディレクトリ。ユーザー登録・認証・JWT発行を一手に担う |
| User Pool Client | アプリが User Pool に接続するための設定。Client ID を持つ |
| IDトークン | ログイン成功時に Cognito が発行する JWT。email / sub などのユーザー情報を含む |
| Cognito Authorizer | API Gateway が IDトークンを検証する認可機能。Lambdaコードが不要で動作する |
| claims | JWT ペイロードに含まれる情報(email / sub / token_use など) |
| FORCE_CHANGE_PASSWORD | 管理者作成ユーザーの初期状態。この状態では initiate-auth がトークンを返さない |
前提条件
| ツール | 確認コマンド | 備考 |
|---|---|---|
| AWSアカウント | — | IAMユーザーまたはルートユーザー |
| AWS CLI v2(テスト用) | aws --version | IDトークン取得・ユーザー作成に使用 |
AWS CLI がない場合は、テスト操作に AWSコンソールの CloudShell を代用できます(後述)。
使用するAWSサービス
| サービス | 役割 | 料金 |
|---|---|---|
| Amazon Cognito | ユーザー管理・認証・JWT発行 | 月5万MAU(月間アクティブユーザー)まで無料 |
| API Gateway | REST API のエンドポイント管理・認可 | 月100万リクエストまで無料(無料期間12ヶ月) |
| Lambda | APIのビジネスロジック(1関数) | 月100万リクエスト・400,000 GB-秒まで無料 |
| IAM | Lambda の実行権限管理 | 無料 |
| CloudWatch Logs | Lambda の実行ログ | 月5GBまで無料 |
全体の作業順序
① Cognito User Pool を作成する(新UI対応)
↓
② テスト用アプリクライアントを作成する(シークレットなし)
↓
③ Lambda 関数を作成する
↓
④ API Gateway REST API を作成する
(/hello・/profile エンドポイント + Cognito Authorizer 設定)
↓
⑤ テストユーザーを作成する
↓
⑥ 動作テスト(401確認 → JWTトークン取得 → 認証成功)
↓
⑦ リソースの削除① Cognito User Pool を作成する
AWSコンソール → Cognito → 「アプリケーションを作成」
新UIの注意:
旧UIにあった「ユーザープールを作成」ボタンは廃止されています。現在は「アプリケーションを作成」からウィザードで User Pool とクライアントをまとめて作成します。
1-1. アプリケーションを定義する
| 設定項目 | 値 |
|---|---|
| アプリケーションタイプ | 従来のウェブアプリケーション |
| アプリケーション名 | MyWebApp |
1-2. オプションを設定する
| 設定項目 | 値 |
|---|---|
| サインイン識別子のオプション | メールアドレス にチェック(電話番号・ユーザー名はチェック不要) |
| 自己登録を有効 | チェックする(デフォルトでチェック済み) |
| サインアップのための必須属性 | 空欄のまま(属性を選択しない) |
サインイン識別子は作成後に変更不可。 「メールアドレス」のみ選択しておくことを推奨します。
1-3. リターン URL(スキップ)
「リターン URL を追加」は入力不要(今回はマネージドログインページを使わないため)。
画面下部の「ユーザーディレクトリを作成する」をクリック。
控えておく情報
作成後、User Pool の概要ページが開きます。
- ユーザープール ID(例:
ap-northeast-1_XXXXXXXX)
→ 概要ページの「ユーザープール情報」欄に表示 - クライアント ID(例:
XXXXXXXXXXXXXXXXXXXXXXXXXX)
→ 左サイドバー「アプリケーション」→「アプリケーションクライアント」→MyWebAppをクリック → 「クライアント ID」欄
注意: 概要ページの「レコメンデーション」欄にも
MyWebAppのリンクが表示されますが、これは Quick Setup ガイドへの誘導です。クライアント ID は左サイドバーから辿ってください。
User Pool 名について:
新UIでは、アプリ名の前にUser pool -が自動で付きます(例:User pool - MyWebApp)。以降の手順では「作成した User Pool」と表記します。
② テスト用アプリクライアントを作成する
新UIの「アプリケーションを作成」で自動作成された MyWebApp クライアントにはクライアントシークレットが自動付与されています。シークレットありのクライアントで CLI から initiate-auth を実行すると SECRET_HASH was not received エラーが出ます。
そのため、シークレットなし・ALLOW_USER_PASSWORD_AUTH 有効の新しいクライアントを別途作成します。
Cognito → 作成した User Pool → 左サイドバー「アプリケーション」→「アプリケーションクライアント」→「アプリケーションクライアントを作成」
2-1. アプリケーションを定義する
| 設定項目 | 値 |
|---|---|
| アプリケーションタイプ | シングルページアプリケーション (SPA) |
| アプリケーション名 | MyWebAppClient |
「SPA」を選ぶ理由:
新UIでは「従来のウェブアプリケーション」を選ぶとクライアントシークレットが必須になります。「SPA」を選ぶとシークレットなしで作成できます。CLI テスト目的ではこちらを使います。
2-2. リターン URL(スキップ)
入力不要。
2-3. Client secret configuration
SPA を選択した場合、この欄は表示されません(シークレットなしで自動確定)。
「アプリケーションクライアントを作成」をクリック。
2-4. 認証フローを確認する
作成後、MyWebAppClient をクリックして詳細画面を開きます。
「認証フロー」セクションに ALLOW_USER_PASSWORD_AUTH が含まれていることを確認します。
含まれていない場合は「編集」→「ALLOW_USER_PASSWORD_AUTH」にチェック → 「変更を保存」。
ALLOW_USER_PASSWORD_AUTH とは:
CLI から username と password を直接指定して認証する「ユーザーパスワード認証」フローです。テスト用に有効化します(本番環境では SRP フローが推奨)。
控えておく情報(更新):
- クライアント ID → 新しく作成した
MyWebAppClientのクライアント ID を使う(① のMyWebAppのクライアント ID は使わない)
③ Lambda 関数を作成する
AWSコンソール → Lambda → 「関数の作成」
| 設定項目 | 値 |
|---|---|
| 作成方法 | 一から作成 |
| 関数名 | CognitoApiFunction |
| ランタイム | Python 3.12 |
| アーキテクチャ | x86_64 |
| 実行ロール | 「基本的な Lambda アクセス権限で新しいロールを作成」(デフォルトのまま) |
「関数の作成」をクリック。
コードの入力
関数ページ → 「コード」タブ → lambda_function.py を開いて既存の内容を全て置き換えて以下を貼り付けます。
import json
def lambda_handler(event, context):
resource = event.get("resource", "/") # API Gateway プロキシ統合: リクエストパスが "resource" に入る
if resource == "/hello":
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"message": "Hello! This is a public endpoint."}),
}
if resource == "/profile":
# event["requestContext"]["authorizer"]["claims"] に JWT ペイロードが格納される
claims = (
event.get("requestContext", {})
.get("authorizer", {})
.get("claims", {})
)
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps(
{
"message": "認証成功",
"email": claims.get("email"),
"sub": claims.get("sub"), # Cognito が生成したユーザーの一意 ID
"token_use": claims.get("token_use"), # "id" が返る(IDトークンを使っているため)
},
ensure_ascii=False,
),
}
return {
"statusCode": 404,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"message": "Not Found"}),
}「Deploy」ボタンをクリックしてコードを保存します。
Lambda 側で認証コードが不要な理由:
API Gateway の Cognito Authorizer が IDトークン(JWT)の検証をすべて担っています。Lambda が呼ばれた時点では認証済みであることが保証されており、検証済みのclaimsだけを受け取って使えばよい設計です。
④ API Gateway REST API を作成する
AWSコンソール → API Gateway → 「APIを作成」
4-1. API タイプの選択
「REST API」の「構築」をクリック。
| 設定項目 | 値 |
|---|---|
| API タイプ | REST API(「プライベート」ではない方) |
| 新しい API の作成 | 「新しい API」 |
| API 名 | CognitoSampleApi |
| エンドポイントタイプ | リージョン |
「APIを作成」をクリック。
4-2. リソース /hello を作成する
左のリソースツリーで /(ルート)を選択 → 「リソースを作成」ボタンをクリック
| 設定項目 | 値 |
|---|---|
| リソースパス(親パス) | /(変更不要) |
| リソース名 | hello |
新UIの注意点:
「リソースパス」はリソースを作成する親パスの選択(/= ルート配下という意味)です。/helloと入力する欄ではありません。「リソース名」にhelloと入力するだけで OK です。
「リソースを作成」をクリック。
/hello を選択した状態で → 「メソッドを作成」→ メソッドタイプ GET を選択
| 設定項目 | 値 |
|---|---|
| 統合タイプ | Lambda 関数 |
| Lambda プロキシ統合の使用 | チェックする |
| Lambda 関数 | CognitoApiFunction |
「保存」→「OK」(Lambda 呼び出し権限の追加確認)。
4-3. リソース /profile を作成する
左のリソースツリーで /(ルート)を選択 → 「リソースを作成」
| 設定項目 | 値 |
|---|---|
| リソースパス(親パス) | /(変更不要) |
| リソース名 | profile |
「リソースを作成」をクリック。
/profile を選択した状態で → 「メソッドを作成」→ メソッドタイプ GET を選択
| 設定項目 | 値 |
|---|---|
| 統合タイプ | Lambda 関数 |
| Lambda プロキシ統合の使用 | チェックする |
| Lambda 関数 | CognitoApiFunction |
「保存」をクリック。
4-4. Cognito Authorizer を作成する
左側メニューの「オーソライザー」→「オーソライザーを作成」
| 設定項目 | 値 |
|---|---|
| オーソライザー名 | CognitoAuthorizer |
| オーソライザーのタイプ | Cognito |
| Cognito ユーザープール(リージョン) | ap-northeast-1(変更不要) |
| Cognito ユーザープール(検索欄) | ① で作成した User Pool を選択(例: User pool - MyWebApp) |
| トークンのソース | Authorization(必ず入力する) |
| トークンの検証(オプション) | 空欄のまま |
新UIの注意点:
「トークンのソース」はデフォルトで空欄になっています。Authorizationと入力しないと動作しません。入力を忘れると全リクエストが 401 になります。
「オーソライザーを作成」をクリック。
動作確認:
「オーソライザーのテスト」→ テスト用トークン欄に適当な文字を入力 → 「テスト」→ 401 が返れば正常(正しいトークンがないため 401 が正解)。
4-5. /profile GET メソッドに Authorizer を設定する
左のリソースツリーで /profile → GET を選択 → 「メソッドリクエストの設定」セクションの「編集」ボタンをクリック
「認可」ドロップダウンを開き、Cognito ユーザープールオーソライザー の配下にある CognitoAuthorizer を選択 → 「保存」
新UIの注意点:
選択肢はなし/AWS IAM/Cognito ユーザープールオーソライザー(カテゴリ)→CognitoAuthorizerの階層構造になっています。CognitoAuthorizerはカテゴリ名ではなく、その下にインデントされた項目を選んでください。
ポイント:
/hello GETには Authorizer を設定しない(認証不要)。/profile GETにのみ CognitoAuthorizer を設定することで、エンドポイントごとに認証要件を分けます。
4-6. API をデプロイする
「アクション」→「API のデプロイ」
| 設定項目 | 値 |
|---|---|
| デプロイされるステージ | 「新しいステージ」 |
| ステージ名 | Prod |
「デプロイ」をクリック。
控えておく情報:
「Prod ステージエディター」に表示される URL の呼び出し(例: https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/Prod)をコピーします。
⑤ テストユーザーを作成する
Cognito → 作成した User Pool → 左サイドバー「ユーザー管理」→「ユーザー」→「ユーザーを作成」
| 設定項目 | 値 |
|---|---|
| 招待メッセージ | 「招待を送信しない」 を選択 |
| E メールアドレス | test@example.com |
| 仮パスワード | 「パスワードの設定」 を選択 |
| パスワード | TestPass1! |
パスワードポリシー(新UIのデフォルト):
最小8文字 / 数字・小文字・大文字・特殊文字をそれぞれ1文字以上含むこと。TestPass1は特殊文字がないため不可。TestPass1!を使ってください。
「ユーザーを作成」をクリック。
ユーザーの「確認ステータス」列に 「パスワードを強制的に変更」 と表示されます(= FORCE_CHANGE_PASSWORD 状態)。
この状態でinitiate-authを実行してもトークンが返らずNEW_PASSWORD_REQUIREDチャレンジが返されます。
次の CLI コマンドで永続パスワードを設定して「確認済み」(CONFIRMED)に変更します。
CLI で永続パスワードを設定する:
方法 A: CloudShell(コンソール版ハンズオンにおすすめ)
AWSコンソール画面下部の「CloudShell」をクリックして起動します。コンソールと同じ認証情報が自動で使われるため追加設定は不要。
aws cognito-idp admin-set-user-password \
--user-pool-id ap-northeast-1_XXXXXXXX \
--username test@example.com \
--password 'TestPass1!' \
--permanent \
--region ap-northeast-1CloudShell は Linux シェルのため
\で改行します。!を含むパスワードはシングルクォート'...'で囲んでください(ダブルクォートだと bash がエラーになる場合があります)。
方法 B: ローカルの AWS CLI(コマンドプロンプト)
aws cognito-idp admin-set-user-password ^
--user-pool-id ap-northeast-1_XXXXXXXX ^
--username test@example.com ^
--password TestPass1! ^
--permanent ^
--region ap-northeast-1
ap-northeast-1_XXXXXXXXを ① で控えたユーザープール ID に置き換えてください。
コマンドが出力なしで返れば成功です。コンソールでユーザーの「確認ステータス」が**「確認済み」**に変わったことを確認してください。
⑥ 動作テスト
テスト1: 公開エンドポイント(/hello)→ 200
curl https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/Prod/hello期待するレスポンス:
{"message": "Hello! This is a public endpoint."}テスト2: 未認証でプライベートエンドポイント(/profile)→ 401
curl https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/Prod/profile期待するレスポンス(API Gateway が返す。Lambda は呼ばれていない):
{"message": "Unauthorized"}テスト3: IDトークンを取得する
aws cognito-idp initiate-auth ^
--auth-flow USER_PASSWORD_AUTH ^
--auth-parameters "USERNAME=test@example.com,PASSWORD=TestPass1!" ^
--client-id XXXXXXXXXXXXXXXXXXXXXXXXXX ^
--region ap-northeast-1
XXXXXXXXXXXXXXXXXXXXXXXXXXを ② で控えたMyWebAppClientのクライアント ID に置き換えてください(① のMyWebAppのクライアント ID ではありません)。
レスポンス例(抜粋):
{
"AuthenticationResult": {
"AccessToken": "eyJra...",
"ExpiresIn": 3600,
"TokenType": "Bearer",
"RefreshToken": "eyJjb...",
"IdToken": "eyJra..."
}
}IdToken の値をコピーしておきます(次のテストで使用)。
IdToken と AccessToken の違い:
トークン 用途 含まれる情報 IdToken ユーザー認証(今回使用) email / sub など AccessToken Cognito API へのアクセス sub / scope など API Gateway の Cognito Authorizer に渡すのは IdToken。AccessToken を渡しても 403 になります。
テスト4: 認証ありでプライベートエンドポイント(/profile)→ 200
IdToken は非常に長いため、変数に格納してから実行します。
方法 A: CloudShell(おすすめ)
TOKEN="eyJra...(IdToken全体を貼り付ける)"
curl -H "Authorization: $TOKEN" https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/Prod/profile方法 B: Windows CMD(変数使用)
set TOKEN=eyJra...(IdToken全体を貼り付ける)
curl -H "Authorization: %TOKEN%" https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/Prod/profile重要: CMD では変数展開に
%TOKEN%を使います。$TOKEN(bash 構文)を CMD で使うと文字列$TOKENがそのまま送られ 401 になります。
またBearerプレフィックスは不要です。raw JWT をそのまま Authorization ヘッダーに指定してください。
期待するレスポンス:
{
"message": "認証成功",
"email": "test@example.com",
"sub": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"token_use": "id"
}
subは Cognito が自動生成したユーザーの一意 ID です。token_use: "id"は IDトークンを使っていることを示します。
⑦ リソースの削除
課金を止めるために、ハンズオン完了後は必ずリソースを削除してください。
1. API Gateway を削除する
API Gateway → APIs → CognitoSampleApi を選択 → 「アクション」→「APIの削除」
2. Lambda 関数を削除する
Lambda → 関数 → CognitoApiFunction を選択 → 「アクション」→「削除」
3. Cognito User Pool を削除する
Cognito → 作成した User Pool → 右上の「ユーザープールを削除」ボタン → ユーザープール名を入力 → 「削除」
ユーザーとアプリクライアントも一緒に削除されます。
4. IAM ロールを削除する(任意)
IAM → ロール → CognitoApiFunction-role-XXXX を検索して削除
5. CloudWatch Logs のロググループを削除する(任意)
CloudWatch → ロググループ → /aws/lambda/CognitoApiFunction → 削除
SAMとの対比
| SAMの記述 | コンソールでやること |
|---|---|
AWS::Cognito::UserPool | Cognito → 「アプリケーションを作成」で User Pool を作成 |
AWS::Cognito::UserPoolClient(シークレットなし) | ② の手順で SPA タイプのクライアント MyWebAppClient を別途作成 |
Auth.DefaultAuthorizer: CognitoAuthorizer | API Gateway → オーソライザーを作成して /profile GET に適用 |
Auth: Authorizer: NONE(/hello 側) | /hello GET には Authorizer を設定しない |
sam delete | API Gateway / Lambda / Cognito / IAM ロール / ロググループを個別に削除 |
トラブルシューティング
| 症状 | 原因 | 対処 |
|---|---|---|
initiate-auth で Client is configured with secret but SECRET_HASH was not received | ① で自動作成された MyWebApp(シークレットあり)のクライアント ID を使っている | ② の手順で MyWebAppClient(シークレットなし)を作成し、そちらの Client ID を使う |
initiate-auth で NotAuthorizedException | ユーザーが FORCE_CHANGE_PASSWORD 状態 | admin-set-user-password --permanent で CONFIRMED に変更する |
initiate-auth で InvalidParameterException | ALLOW_USER_PASSWORD_AUTH が無効 | User Pool Client の認証フロー設定を確認する |
/profile に IDトークンを送っても 401 | Bearer TOKEN 形式で送っている | Bearer を外し raw JWT(eyJra...)をそのまま Authorization ヘッダーに指定する |
/profile に IDトークンを送っても 403 | トークンが期限切れ(1時間で失効) | initiate-auth で新しいトークンを取得し直す |
/profile に IDトークンを送っても 403 | AccessToken を使っている(IdToken ではない) | AuthenticationResult.IdToken を使用しているか確認する |
Lambda の claims が空 | /profile GET に Authorizer が設定されていない | ④ 4-5 の手順で CognitoAuthorizer を設定し直す |
Lambda の resource が / になる | Lambda プロキシ統合が有効になっていない | GET メソッドで「Lambda プロキシ統合の使用」をチェックする |
| Authorizer テストが 500 になる | User Pool が選択されていない、またはトークンのソースが空 | オーソライザーの設定を確認し Authorization を入力し直す |
まとめ
今回のハンズオンで体験できたこと:
| 確認項目 | 内容 |
|---|---|
| Cognito User Pool | ユーザー作成・JWT 発行・パスワードポリシーを新UIで設定 |
| Cognito Authorizer | API Gateway がIDトークンを自動検証 → Lambda は認証コード不要 |
| エンドポイント別認証制御 | /hello(認証なし)と /profile(認証必須)を同じ Lambda で分岐 |
| FORCE_CHANGE_PASSWORD 対処 | admin-set-user-password --permanent で CONFIRMED 状態に変更 |
| 新UI操作 | 「アプリケーションを作成」→ SPA クライアントの追加作成 → Authorizer のトークンソース設定 |
コンソール版とSAM版を比較してみる
コンソールでCognitoとAPI Gatewayの連携を理解したら、SAMで同じ構成をコードで定義することで「SAMが何を自動化しているか」が明確になります。DefaultAuthorizer によるワンライン認証設定や、ALLOW_USER_PASSWORD_AUTH の自動有効化など、コンソールでの手動操作がコードに対応しています。
コメント