Amazon API Gatewayで「認証付きのAPI」を作る方法は、Cognitoオーソライザー、JWTオーソライザー、Lambdaオーソライザー、IAM認可の4つです。どれが使えるかはREST APIかHTTP APIかで変わり、同じCognitoのトークンでもIDトークンとアクセストークンでは判定の仕方が異なる仕様です。本記事では、4方式の対応範囲を表で整理し、CLIとPythonの設定例で動かすところまで示したうえで、認可キャッシュで別のメソッドが403になる落とし穴と、方式を選ぶ条件を言い切ります。API Gateway自体の役割はAPIゲートウェイの役割と導入判断で整理しています。
まとめ:利用者向けはCognitoかJWT、システム間はIAM、例外だけLambda
ブラウザやスマートフォンアプリから一般利用者が呼ぶAPIなら、REST APIはCognitoオーソライザー、HTTP APIはJWTオーソライザーで足ります。どちらもトークンの署名と有効期限をAPI Gatewayが検証し、無効なリクエストはバックエンドに届く前に401で返ります。社内システムやLambda同士の呼び出しは、IAMロールで署名するIAM認可が手間もコストも小さい方式です。
Lambdaオーソライザーは、Cognito以外のIdPをREST APIで使う、ヘッダーとパスの組み合わせで判定する、といった上の3方式で表せない条件のときに限って選びます。自前で書く分だけ、キャッシュの設計と500エラーの切り分けを背負うからです。どの方式でも、オーソライザーが判定するのは「正しい利用者か」までです。そのユーザーが当該データを読んでよいかは、アプリケーション側で確かめる必要があります。
API Gatewayの認証方式4種と、REST API・HTTP APIごとの対応範囲
最初に、どのAPI種別でどの方式が使えるかを確定させます。ここを間違えると、設定画面に目当ての項目が出てきません。
4方式が受け取るもの・判定する主体・対応API種別を1つの表で比較
REST APIとHTTP APIの比較表(公式)の認可オプションを、クライアントが送るものと合わせて並べると次のとおりです(2026年10月時点)。
| 方式 | REST API | HTTP API | クライアントが送るもの | 向く呼び出し元 |
|---|---|---|---|---|
| Cognitoオーソライザー | 対応 | JWTオーソライザー経由 | CognitoのIDトークンかアクセストークン | 一般利用者のWeb・アプリ |
| JWTオーソライザー | 非対応 | 対応 | OIDC・OAuth 2.0のJWT | Cognito・Entra ID等の利用者 |
| Lambdaオーソライザー | 対応 | 対応 | 任意のトークンやヘッダー | 独自の判定条件がある場合 |
| IAM認可 | 対応 | 対応 | SigV4署名付きリクエスト | AWS内のシステム間 |
読み取るべきは2点です。REST APIにはJWTオーソライザーが無く、Cognito以外のIdPのJWTを検証するにはLambdaオーソライザーを書くことになります。逆にHTTP APIには「Cognito」という種別が無く、CognitoのIssuer URLをJWTオーソライザーに登録して使います。
APIキーとリソースポリシーを認証方式に数えないほうがよい理由
設定画面で隣に並ぶAPIキーは、呼び出し量を数えて制限するための識別子です。同じ使用量プランに入った別のAPIまで呼べてしまうため、認可の手段にはなりません。詳細はAPI GatewayのAPIキーと使用量プランの設定で解説しています。
リソースポリシーはREST API限定で、送信元のIPアドレスやVPCエンドポイントで絞る仕組みです。「どこから来たか」は判定できても「誰か」は判定しません。IP制限の組み方はAPI GatewayのIP制限の手順と使い分けにまとめています。どちらも、本記事の4方式に重ねて使う前提の部品です。
CognitoオーソライザーをREST APIのメソッドに付け、IDトークンで呼び出す手順
ユーザープールとアプリクライアントは作成済みとして進めます。まだの場合はCognitoのユーザープール構築からJWT検証までの手順で用意し、同記事にあるcreate-authorizerでオーソライザーを作っておいてください。
update-methodで認証を設定し再デプロイ後にcurlで401と200を確認
オーソライザーは作っただけでは効きません。メソッドのauthorizationTypeとauthorizerIdを書き換え、ステージへ再デプロイして初めて反映されます。Cognitoオーソライザーの公式手順も、変更後の再デプロイを必須としています。
# GET /orders に Cognito オーソライザー(ID: abc123)を付ける
aws apigateway update-method --region ap-northeast-1 \
--rest-api-id abcde12345 --resource-id r1s2t3 --http-method GET \
--patch-operations \
op=replace,path=/authorizationType,value=COGNITO_USER_POOLS \
op=replace,path=/authorizerId,value=abc123
# 変更をステージに反映する(ここを忘れると認証なしで通り続ける)
aws apigateway create-deployment --region ap-northeast-1 \
--rest-api-id abcde12345 --stage-name prod
# トークンなしは 401、IDトークン付きは 200 になることを確かめる
URL=https://abcde12345.execute-api.ap-northeast-1.amazonaws.com/prod/orders
curl -s -o /dev/null -w "%{http_code}\n" "$URL"
curl -s -o /dev/null -w "%{http_code}\n" -H "Authorization: $ID_TOKEN" "$URL"
Cognitoオーソライザーのトークンの読み取り元は、作成時の--identity-sourceで指定したヘッダー(通常はAuthorization)です。1つのオーソライザーに登録できるユーザープールは最大1,000で、この上限は引き上げられません。
スコープを指定するとアクセストークン判定に切り替わる仕様と401の原因
Cognitoオーソライザーは、メソッドに認可スコープを設定したかどうかで、受け取ったトークンの扱いを変えます。スコープが空ならIDトークンとして検証し、スコープを設定するとアクセストークンとして扱い、トークン内のスコープがメソッドのスコープのどれかと一致しなければ401を返します。
# アクセストークンの orders/read スコープを要求する(設定後は再デプロイ)
aws apigateway update-method --region ap-northeast-1 \
--rest-api-id abcde12345 --resource-id r1s2t3 --http-method GET \
--patch-operations op=add,path=/authorizationScopes,value=orders/read
この仕様が原因の401は3パターンに分かれます。スコープ未設定のメソッドにアクセストークンを送る、トークン検証欄にaudの正規表現を入れたままアクセストークンを送る(アクセストークンにはaudが無いため拒否される)、コンソールのテスト呼び出しにアクセストークンを使う(テストはIDトークンのみ対応)、の3つです。外部のバックエンドへ委譲する権限を表すならアクセストークンとスコープ、画面に名前を出す程度ならIDトークン、と役割で分けてください。トークンの構造はJWTの構造と署名検証の仕組みで確認できます。
HTTP APIのJWTオーソライザーをCognitoや外部IdPのトークンで設定する手順
HTTP APIでは、IdPの種類を問わずJWTオーソライザーを使います。登録するのは発行者(Issuer)と受け手(Audience)の2つだけです。
IssuerとAudienceを指定してJWT認証をルートに設定するCLIの実行例
CLIではcreate-authorizerにIssuerとAudienceを渡し、続いてupdate-routeで作成したオーソライザーをルートに設定します。
JWTオーソライザーの公式解説によると、Cognitoを使う場合のIssuerはhttps://cognito-idp.<リージョン>.amazonaws.com/<ユーザープールID>の形式です。
# Cognito ユーザープールを発行者、アプリクライアントIDを受け手として登録する
aws apigatewayv2 create-authorizer --region ap-northeast-1 \
--api-id a1b2c3d4e5 --name cognito-jwt \
--authorizer-type JWT \
--identity-source '$request.header.Authorization' \
--jwt-configuration Audience=1example23456789,Issuer=https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_EXAMPLE
# ルートに付け、アクセストークンの orders/read スコープを必須にする
aws apigatewayv2 update-route --region ap-northeast-1 \
--api-id a1b2c3d4e5 --route-id xyz789 \
--authorization-type JWT --authorizer-id def456 \
--authorization-scopes orders/read
HTTP APIは自動デプロイを有効にしたステージなら再デプロイが要りません。トークンはBearer を付けても付けなくても受け付けられます。CognitoのアクセストークンにはAudienceを表すaudが無くclient_idが入っていますが、JWTオーソライザーはaudが無いときにclient_idを照合するため、アプリクライアントIDをAudienceに登録すれば通ります。
JWTオーソライザーが検証する7クレームと公開鍵キャッシュ最大2時間の影響
JWTオーソライザーは、署名の検証に加えてkid、iss、aud(無ければclient_id)、exp、nbf、iat、scope(またはscp)の7項目を確かめ、1つでも外れれば拒否します。署名アルゴリズムはRSA系のみの対応で、HS256で署名する自前のIdPはそのままでは使えません。
運用で効いてくるのは公開鍵のキャッシュです。API GatewayはIssuerのjwks_uriから取った公開鍵をベストエフォートで最大2時間保持します。IdP側で署名鍵を入れ替えるときは、新旧の鍵がどちらも有効な期間を2時間以上取ってください。呼び出し頻度の低いAPIほどキャッシュが切れやすく、その都度の鍵の取得が初回リクエストの遅延になります。
公式は、IDトークンとアクセストークンを見分ける標準の仕組みが無いことから、IDトークンでの認可が必要な場合を除き、ルートにスコープを必須にするよう推奨しています。スコープを付けないルートでは、同じIssuerとAudienceのIDトークンでも通る点を覚えておいてください。
Lambdaオーソライザーの自前実装でのTOKEN・REQUEST選択とキャッシュ設計
Lambdaオーソライザーは、API Gatewayが呼び出し元の情報を関数に渡し、関数が返したIAMポリシーで許可か拒否かを決める方式です。自由度と引き換えに、設計を誤ったときの症状が分かりにくくなります。
外部IdPのJWTをREST APIで検証するPythonのREQUEST認証実装例
種類はトークン1つを受け取るTOKENと、ヘッダー・クエリ文字列・ステージ変数・コンテキスト変数を組み合わせて受け取るREQUESTの2つです。Lambdaオーソライザーの公式解説は、複数の識別ソースでキャッシュキーを分けられるREQUESTを推奨しています。次の例は、Auth0やEntra IDなど外部IdPのJWTをPyJWTのPyJWKClientで検証します。
import os
import jwt
from jwt import PyJWKClient
# IdP の JWKS エンドポイントから公開鍵を取得・キャッシュする(コールドスタート時に1回生成)
JWKS = PyJWKClient(os.environ["JWKS_URL"])
ISSUER = os.environ["ISSUER"]
AUDIENCE = os.environ["AUDIENCE"]
def lambda_handler(event, context):
headers = {k.lower(): v for k, v in (event.get("headers") or {}).items()}
token = headers.get("authorization", "").removeprefix("Bearer ")
try:
key = JWKS.get_signing_key_from_jwt(token)
claims = jwt.decode(token, key.key, algorithms=["RS256"],
audience=AUDIENCE, issuer=ISSUER)
except jwt.PyJWTError:
raise Exception("Unauthorized") # API Gateway は 401 を返す
# methodArn = arn:aws:execute-api:リージョン:アカウント:APIID/ステージ/メソッド/パス
api_arn, stage = event["methodArn"].split("/")[:2]
return {
"principalId": claims["sub"],
"policyDocument": {
"Version": "2012-10-17",
"Statement": [{
"Action": "execute-api:Invoke",
"Effect": "Allow",
# キャッシュを効かせるため、このステージの全メソッドを許可する
"Resource": f"{api_arn}/{stage}/*/*",
}],
},
# 値は文字列化されてバックエンドに渡る(オブジェクトや配列は不可)
"context": {"tenant": str(claims.get("tenant_id", ""))},
}
関数を作ったら、aws apigateway create-authorizer --type REQUEST --identity-source method.request.header.Authorization --authorizer-result-ttl-in-seconds 300でオーソライザーとして登録し、aws lambda add-permissionでAPI Gatewayからの呼び出しを許可します。この許可が無いと、利用者には500が返ります。
認可キャッシュTTL既定300秒・最大3600秒とmethodArn限定許可の落とし穴
認可キャッシュのTTLはCreateAuthorizerのAPIリファレンスで既定300秒、最大3600秒(1時間)、0で無効と定められています。キャッシュが効くと、同じトークンの2回目以降は関数が呼ばれず、保存済みのポリシーで判定されます。
典型的な失敗は、公式サンプルのとおりポリシーのResourceにevent["methodArn"]だけを入れることです。最初にGET /ordersを呼ぶと「GET /ordersだけ許可」のポリシーがキャッシュされ、同じトークンで続けてGET /usersを呼ぶと、関数が呼ばれないまま403になります。公式も、キャッシュを有効にするならAPI全体のメソッドに通用するポリシーを返し、メソッドごとに変えたいならキャッシュを切るよう書いています。上の例でワイルドカードを返しているのはこのためです。HTTP APIでルートごとにキャッシュを分けたい場合は、識別ソースに$context.routeKeyを足します。
401・403・500の応答とLambdaオーソライザーのエラーログによる原因調査
エラーログには$context.authorizer.errorを使い、応答コードと照合して原因を調べます。
Lambdaオーソライザーのステータスコードは、関数の終わり方で決まります。実務ではまず500を疑うと早く片付きます。
- 500:関数が
Unauthorized以外の例外で終わった、戻り値の形式が不正、API Gatewayに関数の呼び出し権限が無い - 401:関数が
Unauthorizedで終わった、またはキャッシュ有効時に識別ソースのヘッダーが欠けていて関数自体が呼ばれていない - 403:関数が
Denyのポリシーを返した、またはキャッシュ済みのポリシーが今回のメソッドを含んでいない
500の原因は、アクセスログの書式に$context.authorizer.errorを加えると記録されます。アクセスログの設定手順はAPI Gatewayのログ設定とX-Rayでの障害調査を参照してください。401なのに関数のログが1行も無い場合は、識別ソースのヘッダー名の綴りを最初に確認します。
IAM認可でSigV4署名を付け、社内システムやLambdaからAPIを呼び出す設定
呼び出し元がAWS上のLambdaやECSタスクなら、トークンを発行する仕組み自体が要りません。呼び出し元のIAMロールでリクエストに署名し、API GatewayがIAMで権限を確かめます。
AWS_IAMを付けたメソッドとexecute-api:Invokeで許可するポリシー例
メソッド側はauthorizationTypeをAWS_IAMにし、再デプロイします(HTTP APIならaws apigatewayv2 update-route --authorization-type AWS_IAM)。呼び出し元のロールには、API呼び出しのIAMポリシー形式に沿ってexecute-api:Invokeを付けます。
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:ap-northeast-1:123456789012:abcde12345/prod/GET/orders"
}]
}
公式は、メソッドにAWS_IAMを設定しなければIAMポリシーは効かず、そのメソッドは公開状態になると注意しています。ポリシーを作っただけで守った気にならないよう、未署名のcurlが403になることを必ず確認してください。
boto3の認証情報とbotocoreのSigV4Authで署名するPython例
署名の方式はAWS Signature Version 4(公式)で、サービス名にはexecute-apiを指定します。Lambda上なら実行ロールの認証情報がそのまま使えます。
import urllib.request
import boto3
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest
URL = "https://abcde12345.execute-api.ap-northeast-1.amazonaws.com/prod/orders"
# 実行ロール(またはローカルのプロファイル)の認証情報で署名ヘッダーを付ける
creds = boto3.Session().get_credentials()
req = AWSRequest(method="GET", url=URL)
SigV4Auth(creds, "execute-api", "ap-northeast-1").add_auth(req)
with urllib.request.urlopen(urllib.request.Request(URL, headers=dict(req.headers))) as res:
print(res.status, res.read().decode())
キーの配布も失効の運用も不要で、権限の付け外しはIAMロールのポリシー変更だけで済みます。一方、ブラウザから直接呼ぶ構成ではAWSの認証情報を利用者に渡すことになるため、この方式は選びません。
API Gatewayの認証方式を選ぶ条件と、オーソライザーを足さないほうがよい場面
ここまでの仕様を踏まえ、方式の選び方を条件で言い切ります。
呼び出し元が人かシステムか、IdPがCognitoかで決める判断の順番
- 呼び出し元がAWS上のシステムなら、IAM認可にする。
- 人が使う画面からの呼び出しで、IdPがCognitoなら、REST APIはCognitoオーソライザー、HTTP APIはJWTオーソライザーにする。
- IdPがEntra IDやAuth0などのOIDC準拠で、RSA署名なら、HTTP APIのJWTオーソライザーにする。REST APIでしか使えない機能(WAFやAPIキー)が要るときだけ、REST APIとLambdaオーソライザーの組み合わせにする。
- どれにも当てはまらない独自トークンや、ヘッダーとパスの組み合わせによる判定が必要なら、Lambdaオーソライザーを書く。
上から順に当てはめれば、ほとんどのAPIで選ぶ方式は1〜3のいずれかです。既存の社内IdPとのフェデレーションや、複数システムのID統合まで含めて設計する場合は、認証基盤・ID管理システム開発でご相談いただけます。製品に依存しない方式選定やトークン寿命の決め方はAPI認証の設計と方式選定マトリクスにまとめています。
Lambdaオーソライザーを見送る条件と、データ単位の認可をアプリに残す範囲
CognitoかJWTオーソライザーで済む要件に、Lambdaオーソライザーを選ぶのは過剰です。関数の実行時間とコールドスタートが認証の待ち時間に加わり、JWKSの取得やキャッシュ設計、500の切り分けまで自前で抱えます。「将来の要件に備えて」という理由だけでは選びません。
どの方式を選んでも、オーソライザーに任せられるのは「有効な利用者か」「このメソッドを呼べるロールか」までです。注文IDを書き換えて他人の注文を読む、といったデータ単位の認可は、トークンのsubや受け渡したcontextをバックエンドで照合しなければ防げません。オーソライザーに業務ルールまで詰め込むと、キャッシュとの整合が崩れて403が散発します。認証はゲートウェイ、データ単位の認可はアプリ、と線を引いてください。
よくある質問
API Gatewayの認証設定で、よく出る疑問をまとめました。
CognitoオーソライザーとJWTオーソライザーは何が違いますか?
CognitoオーソライザーはREST API専用で、Cognitoのユーザープールしか登録できません。JWTオーソライザーはHTTP API専用で、Cognitoを含むOIDC・OAuth 2.0準拠のIdPのIssuerを登録できます。挙動の差として、Cognitoオーソライザーはスコープ未設定ならIDトークンとして扱い、JWTオーソライザーはIssuerとAudienceが合えばトークンの種類を区別しません。どちらを使うかは、APIをRESTとHTTPのどちらで作るかで自動的に決まります。
ログインできているのにアクセストークンで呼ぶと401になるのはなぜですか?
REST APIのCognitoオーソライザーで、メソッドに認可スコープを設定していないのが最も多い原因です。スコープ未設定のメソッドは受け取ったトークンをIDトークンとして検証するため、アクセストークンは通りません。アクセストークンで呼ぶならメソッドにスコープを設定して再デプロイし、トークン検証欄にaudの正規表現を入れている場合は外してください。
1つのAPIで認証が必要なルートと不要なルートを分けられますか?
認証が必要なルートと不要なルートは分離可能です。オーソライザーはREST APIならメソッド単位、HTTP APIならルート単位で付け外しするため、ヘルスチェック用のルートだけ認証なしにする構成は普通に組めます。REST APIでCORSの事前確認(OPTIONS)を受けるメソッドにオーソライザーを付けると、ブラウザの事前確認が401で失敗するので外しておきます。認証なしのルートを作るときは、意図せず公開していないかを一覧で確認してください。
オーソライザーが検証した利用者の情報をバックエンドで受け取るには?
REST APIのCognitoオーソライザーは$context.authorizer.claimsでトークンのクレームをマッピングテンプレートに渡せます。HTTP APIのJWTオーソライザーなら、Lambda統合のイベントのrequestContext.authorizer.jwt.claimsに入ります。Lambdaオーソライザーは返したcontextがプロキシ統合のイベントに渡りますが、値はすべて文字列化され、オブジェクトや配列は入れられません。
オーソライザーを付けると追加料金はかかりますか?
API Gatewayの公式料金ページに、オーソライザー単体の課金項目は載っていません(2026年10月時点)。Lambdaオーソライザーは関数の実行としてLambdaの料金が掛かり、認可キャッシュを有効にすれば実行回数を減らせます。Cognitoは利用者数に応じた料金が別途必要です。API Gateway本体の単価差はAPI Gatewayの料金を東京リージョンの実額で計算する記事で比較しています。
関連記事
- Cognitoで認証基盤を実装する手順:ユーザープール構築からJWT検証まで:オーソライザーに登録するユーザープールとアプリクライアントの作り方。
- APIゲートウェイとは?役割・機能とリバースプロキシ/サービスメッシュとの違い・導入判断を解説:認証をゲートウェイに寄せる理由と導入判断。
- API認証の設計|方式選定マトリクスとトークン寿命・監査ログの決め方:製品に依存しない方式選定とトークン寿命の決め方。
- JWTとは?構造・署名検証の仕組みとセッション・OAuth/OIDCとの違いを実装視点で解説:オーソライザーが検証するクレームと署名の仕組み。
- API GatewayのAPIキー:使用量プランでスロットリングとクォータを設定する手順と認可に使わない理由:認証の後段で呼び出し量を制限する設定。