---
title: "CloudFrontとCognitoで認証を掛ける手順：Lambda@Edge実装と署名付きCookieの比較"
url: "https://www.issoh.co.jp/tech/details/18506/"
published: 2026-10-18
updated: 2026-10-18
categories: ["AWS"]
publisher: "株式会社一創"
---

# CloudFrontとCognitoで認証を掛ける手順：Lambda@Edge実装と署名付きCookieの比較

CloudFrontには、ALBやAPI GatewayのようにCognitoを選ぶだけで認証を掛けられる設定項目がありません。S3の静的サイトや社内向けドキュメントを「ログインした人だけに見せる」には、Lambda@Edgeでリクエストごとにトークンを検証するか、CloudFrontの署名付きCookieで到達範囲を絞るかのどちらかを組みます。この記事では、AWSが公開している`cognito-at-edge`を使ったLambda@Edgeの実装とデプロイを手順で示し、us-east-1や番号付きバージョンといったLambda@Edge固有の制約を公式ドキュメントの値で整理します。後半の判断対象は、署名付きCookieとの使い分けと、CloudFrontでの認証を採用する条件・見送る場面です。

## まとめ：Cognitoのユーザーで守るならLambda@Edge、期限付き配布なら署名付きCookie

Cognitoのユーザープールでログインさせ、MFAや外部IdPのSSOまで効かせたいなら、ビューアーリクエストにLambda@Edgeを関連付けてJWTを検証する構成を選びます。実装はライブラリで数十行に収まりますが、関数はus-east-1に置き、番号付きバージョンをディストリビューションに関連付け、設定値はコードに埋め込む必要があります。

一方、購入者や招待した相手にファイルを一定期間見せるだけなら、署名付きCookieのほうが単純です。CloudFront自身が署名を検証するため、リクエストごとの関数実行が発生しません。ユーザー管理が要るかどうかで、どちらを組むかが決まります。

## CloudFrontでCognito認証を掛ける仕組みとビューアーリクエストの検証の流れ

CloudFrontでの認証は、エッジで動く関数がCookieを見て「通す」か「ログイン画面へ飛ばす」かを決める仕組みです。CloudFrontのエッジ機能全体の位置づけは[Amazon CloudFrontとは？仕組み・料金プランとエッジ機能・採用判断](https://www.issoh.co.jp/tech/details/15431/)で整理しています。

### 未ログインは302でマネージドログインへ送りCookieのJWTで通す4段階

Lambda@Edgeをビューアーリクエストに関連付けると、キャッシュに当たるリクエストも含めて毎回関数が呼ばれます。流れは次の4段階です。

1. リクエストのCookieにCognitoのトークンが無ければ、Cognitoのマネージドログインへ302でリダイレクトする
2. ログイン後、Cognitoが認可コードを付けて配信ドメインへ戻す
3. 関数が認可コードをトークンに交換し、Cookieに入れて元のURLへ戻す
4. 以降のリクエストはCookieのJWTの署名と期限を検証し、通ればS3などのオリジンへ転送する

検証がキャッシュより前で行われるため、一度キャッシュされた会員向けページが未ログインの相手へ返る事故は起きません。Cognito側の認可コードフローやJWTの検証項目は、親記事の[Cognitoで認証基盤を実装する手順：ユーザープール構築からJWT検証まで](https://www.issoh.co.jp/tech/details/17445/)を前提にしています。

### CloudFront Functionsの制約とCognitoのRS256署名検証

より安く速いCloudFront Functionsで済ませたくなりますが、Cognitoのトークン検証には向きません。[CloudFront FunctionsのJavaScriptランタイム2.0](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-20.html)が提供する`crypto`は、ハッシュとHMAC（md5・sha1・sha256）だけです。CognitoのJWTはRSAの公開鍵署名なので、この範囲では検証できません。

ネットワーク呼び出しもできないため、認可コードをトークンへ交換する処理も書けません。関数サイズは10KB、メモリは2MBが上限です。CloudFront Functionsは「HMACで自前発行したトークンの簡易チェック」や「末尾の`/`に`index.html`を補う書き換え」に回し、Cognito連携はLambda@Edgeに任せます。

## Cognito側の準備手順｜配信ドメインをコールバックURLに登録するクライアント作成

CloudFront用には専用のアプリクライアントを1つ作ります。SPAやモバイルアプリ用のクライアントと共用すると、コールバックURLやトークンの有効期限を変えたときに別のアプリへ影響が出るためです。

### シークレット付きクライアントとコールバックURLをCLIで作る例

`cognito-at-edge`は、既定ではドメインのルートで認可コードを受け取ります。そのため、コールバックURLには配信ドメインのルート（末尾`/`付き）を登録します。[create-user-pool-clientのCLIリファレンス](https://docs.aws.amazon.com/cli/latest/reference/cognito-idp/create-user-pool-client.html)の引数で書くと次のとおりです。

```
# CloudFront専用のアプリクライアント（シークレットあり・認可コードフロー）
aws cognito-idp create-user-pool-client \
  --region ap-northeast-1 \
  --user-pool-id ap-northeast-1_EXAMPLE \
  --client-name cloudfront-docs \
  --generate-secret \
  --allowed-o-auth-flows code \
  --allowed-o-auth-scopes openid email profile \
  --allowed-o-auth-flows-user-pool-client \
  --supported-identity-providers COGNITO \
  --callback-urls https://docs.example.co.jp/ \
  --logout-urls https://docs.example.co.jp/signed-out.html \
  --refresh-token-validity 30
```

ユーザープール自体は東京リージョンに置いたままで構いません。関数がus-east-1にあっても、トークンの検証はユーザープールのリージョンの公開鍵を取りに行くだけだからです。`--refresh-token-validity`の30日は、後述するCookieの期限と合わせるための値です。

### シークレットを付けるかはCookieのHttpOnlyと一緒に決める

クライアントシークレットを付けるかどうかは、ブラウザのJavaScriptからトークンを読ませるかで決まります。[aws-samplesのcloudfront-authorization-at-edge](https://github.com/aws-samples/cloudfront-authorization-at-edge)も、SPAモード（既定）ではシークレットなし・HttpOnlyなし、静的サイトモードではシークレットあり・HttpOnly Cookieとし、後者を安全側として勧めています。

静的なHTMLやPDFを配るだけなら、JavaScriptからトークンを読む必要はありません。シークレットを付け、Cookieを`HttpOnly`にする構成を既定にします。クライアント側でAmplifyのAuthを動かすSPAだけが例外です。

## Lambda@Edgeとcognito-at-edgeの実装・番号付き版の公開手順

関数本体は[awslabs/cognito-at-edge](https://github.com/awslabs/cognito-at-edge)に任せます。トークンの交換・検証・Cookieの発行・リフレッシュを引き受けるライブラリで、npm registry上の最新は2026年10月時点で1.5系です。

### 設定値をコードに埋め込むindex.jsとHttpOnlyを有効にする設定

Lambda@Edgeでは環境変数が使えないため、ユーザープールIDなどの設定値はコードに書きます。`httpOnly`の既定値は`false`なので、明示的に`true`を渡します。

```
// index.js（Node.js・us-east-1にデプロイする）
const { Authenticator } = require('cognito-at-edge');

// Lambda@Edgeは環境変数を使えないため、設定値はここに直接書く
const authenticator = new Authenticator({
  region: 'ap-northeast-1',                 // ユーザープールのリージョン
  userPoolId: 'ap-northeast-1_EXAMPLE',
  userPoolAppId: '1example23456789',
  userPoolAppSecret: 'CLIENT_SECRET',       // 後述の注意を参照
  userPoolDomain: 'docs-auth.auth.ap-northeast-1.amazoncognito.com',
  cookieExpirationDays: 30,                 // refresh-token-validityと合わせる
  httpOnly: true,                           // 既定はfalse
  sameSite: 'Lax',
  logoutConfiguration: {
    logoutUri: '/logout',
    logoutRedirectUri: 'https://docs.example.co.jp/signed-out.html',
  },
  logLevel: 'warn',
});

exports.handler = async (request) => authenticator.handle(request);
```

`cookieExpirationDays`の既定値は365日で、READMEはアプリクライアントの`refreshTokenValidity`と合わせるよう勧めています。合わせないと、Cookieは残っているのにリフレッシュトークンが切れて再ログインになる、という分かりにくい挙動になります。

コードにシークレットを置くことには注意が要ります。関数のコードはLambdaの`GetFunction`権限を持つ人なら取得できます。デプロイ用のアカウントと権限は最小限に絞ってください。[AWSブログの署名付きCookie構成](https://aws.amazon.com/jp/blogs/news/signed-cookie-based-authentication-with-amazon-cloudfront-and-aws-lambdaedge-part-1-authentication/)のように、パラメータストアの`SecureString`から初回だけ読み込む設計に変える方法もあります。

### us-east-1・番号付きバージョン・信頼ポリシーの3つの必須条件

[Lambda@Edgeの制約の公式ドキュメント](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-at-edge-function-restrictions.html)で、デプロイ時に必ず引っかかる条件は次の3つです。

- 関数は米国東部（バージニア北部）＝us-east-1に作る
- 関連付けるのは番号付きバージョンで、`$LATEST`やエイリアスは使えない
- 実行ロールの信頼ポリシーに`lambda.amazonaws.com`と`edgelambda.amazonaws.com`の両方を入れる

このほか、VPC接続・レイヤー・arm64・コンテナイメージ・X-Ray・プロビジョンド同時実行も使えません。依存ライブラリは`node_modules`ごとzipに含め、x86\_64で作ります。信頼ポリシーと関連付けに要る権限は[Lambda@EdgeのIAM設定](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-permissions.html)に例があります。

```
# 依存を含めてzip化し、us-east-1に関数を作ってバージョン1を発行する
npm install cognito-at-edge
zip -r function.zip index.js node_modules

aws lambda create-function \
  --region us-east-1 \
  --function-name docs-cognito-auth \
  --runtime nodejs22.x \
  --architectures x86_64 \
  --handler index.handler \
  --role arn:aws:iam::123456789012:role/lambda-edge-docs-auth \
  --zip-file fileb://function.zip

aws lambda publish-version \
  --region us-east-1 \
  --function-name docs-cognito-auth
```

[publish-version](https://docs.aws.amazon.com/cli/latest/reference/lambda/publish-version.html)が返す`FunctionArn`は末尾に`:1`のような版番号が付いた形で、これを次の手順でディストリビューションに渡します。ランタイムは作成時点でLambda@Edgeが受け付けるNode.jsの版を選び、廃止予定日はLambdaのランタイム一覧で確認してください。

## ディストリビューションへの関連付けとログアウト・ログ確認の運用手順

関数を作っただけでは何も守られません。キャッシュビヘイビアのビューアーリクエストに関連付けて、初めて認証が掛かります。

### viewer-requestへの関数関連付けと版付きARNの設定手順

`aws cloudfront get-distribution-config`で取得したJSONの`DefaultCacheBehavior`に次のブロックを入れ、`ETag`を`--if-match`に渡して`update-distribution`を実行します。S3オリジンの作り方とOACの設定は[CloudFrontとS3で静的サイトを公開する手順：OAC・独自ドメイン・デプロイまでCLIで構築](https://www.issoh.co.jp/tech/details/18064/)の構成をそのまま使えます。

```
"LambdaFunctionAssociations": {
  "Quantity": 1,
  "Items": [
    {
      "LambdaFunctionARN": "arn:aws:lambda:us-east-1:123456789012:function:docs-cognito-auth:1",
      "EventType": "viewer-request",
      "IncludeBody": false
    }
  ]
}
```

コードを直したら、新しい版を発行してARN末尾の番号を差し替え、もう一度ディストリビューションを更新します。`$LATEST`を指せない以上、「関数だけ更新して反映されない」のはこの差し替え漏れが原因です。ログイン画面を公開したくないパス（`/signed-out.html`や`/favicon.ico`）は、関数を関連付けない別のキャッシュビヘイビアに分けます。

### ログアウトURLとCognitoの/logoutで両方のセッションを切る

`logoutConfiguration`を設定すると、`/logout`へのアクセスでライブラリがトークンを失効させ、Cookieを削除してから指定先へ戻します。ここで戻り先を認証付きのパスにすると、すぐログイン画面へ飛ばされて「ログアウトしました」を表示できません。戻り先は前の手順で関数を外したパスに置きます。

Cookieを消してもCognito側のセッションが残っていると、次のアクセスでパスワードを聞かれずに再ログインします。共有端末で使う画面なら、Cognitoの[ログアウトエンドポイント](https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html)へ`client_id`と`logout_uri`を付けてリダイレクトし、両方のセッションを終わらせます。`logout_uri`に指定する値は、アプリクライアントの許可済みサインアウトURLと同じURLです。

### ログは実行された地域のCloudWatchに出る点と503の見分け方

Lambda@Edgeのログはus-east-1ではなく、関数が実行された地域のCloudWatch Logsに出ます。aws-samplesのREADMEによると、ロググループ名は`/aws/lambda/us-east-1.関数名`の形式です。日本からのアクセスなら、まず東京リージョンのロググループを見ます。

関数が例外を投げたりタイムアウトしたりすると、利用者には503が返ります。ビューアー系イベントで関数が返せるレスポンスは[CloudFrontのクォータ](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html)上ヘッダーとボディ込みで40KBまでで、超えると502です。ログイン画面のHTMLを関数から返すような拡張はせず、リダイレクトだけを返す設計に留めます。

## 署名付きCookieとの比較｜ユーザー管理の要否とリクエストごとの実行で選び分ける

CloudFrontには、Lambda@Edgeを使わずにアクセスを絞る署名付きURLと署名付きCookieがあります。どちらを選ぶかで、運用するものが変わります。

### CloudFrontが署名を検証する署名付きCookieの仕組みと3つのCookie

[署名付きCookieの公式ドキュメント](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-cookies.html)によると、アプリは利用者がアクセスしてよいと判断した時点で、3つの`Set-Cookie`ヘッダーを返します。CloudFrontはキーグループに登録した公開鍵で署名を検証し、ポリシーの期限やIPアドレス条件を満たせば配信します。URLを変えずに会員エリアの複数ファイルをまとめて守れる点が、1ファイル単位の署名付きURLとの違いです。S3の署名付きURLとは別物で、S3側の仕組みは[AWS S3の使い方｜CLIでのバケット作成から権限設計・署名付きURL](https://www.issoh.co.jp/tech/details/17489/)で扱っています。

関数を毎回実行しない代わりに、Cookieを発行するアプリと秘密鍵の管理は自前です。公式ドキュメントは、`Domain`属性を最も狭い値にすること、`Secure`を付けること、`CloudFront-Expires`を必要最小限の期限にすることを勧めています。

### Lambda@Edge＋Cognitoと署名付きCookieを5つの観点で比較する表

| 観点            | Lambda@Edge＋Cognito | 署名付きCookie       |
| ------------- | ------------------- | ---------------- |
| 利用者の管理        | Cognitoのユーザープール     | 自前のアプリで判断        |
| MFA・外部IdPのSSO | Cognitoの機能で使える      | Cookie発行側で別途実装   |
| リクエストごとの処理    | 毎回関数が実行される          | CloudFrontが署名を検証 |
| 鍵・設定の置き場所     | 関数コードに埋め込む          | キーグループと秘密鍵       |
| 向く用途          | 社内ドキュメント・会員サイト      | 購入者向けの期限付き配布     |

判断の軸は1つで、「誰がログインしたかを管理したいか」です。社員や会員を識別し、退職者を止めたりMFAを強制したりするならLambda@Edge＋Cognitoを選びます。決済や招待メールの時点でアクセス権が決まり、あとは期限まで見せるだけなら署名付きCookieで対応できる範囲です。Lambda@Edgeは同時実行の上限がリージョンあたり1,000で、全リクエストで関数が動く点も、アクセスの多いサイトでは署名付きCookieへ寄せる理由になります。

## CloudFrontでCognito認証を採用する条件と見送る場面｜ALB認証との比較

CloudFrontでの認証は、オリジンに手を入れられない静的コンテンツを守るための手段です。動的なアプリまでこの方式に寄せると、関数の制約がそのまま設計の制約になります。

### S3の静的サイトや社内ドキュメントにLambda@Edgeを選ぶ3つの条件

次の条件がそろうなら、Lambda@Edge＋Cognitoを選びます。

- オリジンがS3など、認証のコードを置けない静的コンテンツである
- 利用者をCognitoのユーザープールか、フェデレーションした社内IdPで識別したい
- ログインできれば同じ範囲を見られる、またはキャッシュビヘイビアのパス単位で分ければ足りる

社内規程のポータルや顧客向けのマニュアルサイト、ビルド済みのSPAをS3で配信しているケースがこれに当たります。CognitoのグループでURLごとの閲覧権限を変えたい、既存の社内IdPとSSOでつなぎたいといった要件まで含めて設計を任せたい場合は、[認証基盤・ID管理システム開発](https://www.issoh.co.jp/service/system/identity/)として要件整理から対応できます。

### オリジンがALBの動的アプリならALBの認証アクションへ寄せる

オリジンがALB配下のWebアプリなら、CloudFrontで認証せず、ALBのリスナールールに`authenticate-cognito`を設定するほうが運用が軽くなります。us-east-1への関数デプロイも版番号の差し替えも要らず、アプリでのユーザー識別に使えるのは署名付きヘッダーです。手順は[ALBとCognitoで認証を後付けする手順：リスナールール設定とヘッダー検証](https://www.issoh.co.jp/tech/details/18501/)にまとめています。

この構成でCloudFrontを前段に置く場合は、ヘッダー・クエリ文字列・Cookieをすべてオリジンへ転送し、認証済みページをキャッシュしない設定が要ります。画面ごとのロール制御がアプリの中心になるなら、エッジでもALBでもなく、アプリ側でJWTを検証する設計に切り替えます。

## CloudFrontとCognitoの認証構成・デプロイ・運用についてよくある質問と回答

CloudFrontにCognito認証を掛けるときに出やすい疑問を、公式ドキュメントとライブラリのREADMEの記載をもとに回答します。

### Lambda@Edgeの関数は東京リージョンに作れませんか？

作れません。Lambda@Edgeとして関連付けられるのはus-east-1の関数だけで、CloudFrontが世界中のエッジへ複製して実行します。ユーザープールは東京に置いたままで問題なく、関数のコードに`region: 'ap-northeast-1'`と書けば東京のユーザープールの公開鍵で検証します。CloudFormationやCDKで東京向けのスタックを組んでいる場合は、関数だけus-east-1の別スタックに分けてください。

### S3のバケットに直接アクセスされると認証を回避されませんか？

バケットを公開していると回避されます。認証はCloudFrontを通ったリクエストにしか掛からないため、S3はパブリックアクセスをブロックし、OAC経由でCloudFrontからだけ読めるようにします。バケットポリシーの書き方は[CloudFront OAC（オリジンアクセスコントロール）とは？OAIとの違いとS3バケットポリシー設定手順](https://www.issoh.co.jp/tech/details/3684/)を参照してください。ウェブサイトエンドポイントをオリジンにするとOACが使えない点にも注意が要ります。

### ログイン画面を日本語化したり自社ロゴを表示できますか？

できます。表示されるのはCognitoのマネージドログインなので、Cognito側のブランディング設定で変更します。CloudFrontやLambda@Edge側で画面を組み立てる必要はありません。手順は[AWS Cognito Hosted UI（マネージドログイン）のカスタマイズ・日本語化と実装手順](https://www.issoh.co.jp/tech/details/3441/)にまとめています。独自ドメインでログイン画面を出す場合も、設定するのはユーザープールのドメインです。

### 関数を更新したのに古い動きのままなのはなぜですか？

ディストリビューションが古い版番号のARNを指したままになっているのが、典型的な原因です。Lambda@Edgeは`$LATEST`を関連付けられないため、コードを変えたら`publish-version`で新しい版を発行し、`LambdaFunctionARN`の末尾を差し替えて`update-distribution`を実行します。更新がエッジへ行き渡るまでの数分間は、新旧の版が混在します。

### Cognitoのグループで閲覧できるページを分けられますか？

パス単位なら可能です。aws-samplesの構成では`UserPoolGroupName`を指定すると、アクセスできる利用者は、そのグループに所属するユーザーだけです。`cognito-at-edge`を使う場合は、検証済みトークンの`cognito:groups`を見て判定する処理を自前で足し、キャッシュビヘイビアごとに別の関数を関連付けます。グループの数だけ関数と版の管理が増えるため、3つを超えるならアプリ側の認可へ寄せるほうが保守しやすくなります。

## 関連記事

- [Cognitoで認証基盤を実装する手順：ユーザープール構築からJWT検証まで](https://www.issoh.co.jp/tech/details/17445/)：CloudFront連携の前提になるユーザープールとJWT検証の全体像。
- [Amazon CloudFrontとは？仕組み・料金プランとエッジ機能・採用判断を実装者目線で解説](https://www.issoh.co.jp/tech/details/15431/)：CloudFront FunctionsとLambda@Edgeの位置づけ。
- [CloudFrontとS3で静的サイトを公開する手順：OAC・独自ドメイン・デプロイまでCLIで構築](https://www.issoh.co.jp/tech/details/18064/)：認証を掛ける前の配信構成。
- [ALBとCognitoで認証を後付けする手順：リスナールール設定とヘッダー検証](https://www.issoh.co.jp/tech/details/18501/)：オリジンが動的アプリの場合の選択肢。
- [認証・ID管理とは？MFA・SSO・OIDC・IDaaSの違いと選定を解説](https://www.issoh.co.jp/column/details/13631/)：OIDCやSSOなど認証方式の選び方。

---

出典: [CloudFrontとCognitoで認証を掛ける手順：Lambda@Edge実装と署名付きCookieの比較](<https://www.issoh.co.jp/tech/details/18506/>)（株式会社一創）
