こんにちは、ひるたんぬです。
最近は自分が食べたいと思ったものを、レシピを参考に作ることにハマっています。
レシピを見る際に、必ず「○人前」と書かれていますが、あれは何を基準としているのでしょうか?
調べてみても具体的な基準などは見当たらないのですが、大体乾麺のパスタだと、80〜100gというレシピが多い印象です。
※ ご存知の方がいらっしゃいましたら、何らかの手段や媒体でご教示・発信いただけると幸いです。。。
私はこの量では満足することができないので、「私は人じゃない…?」と疑いかけますが、それは穿った見方ですね。
ただ、「一人前」と言う表現は個人差が大きいと思うので、もっと良い表現があってもいいのになぁ…と思った今日このごろです。
さて、今回はタイトルにもある通り、Amazon CloudFrontとAmazon S3を用いた静的コンテンツ配信基盤において、cognito-at-edgeで特定のパスにのみ認証を設定する方法をご紹介します。
やりたいこと
静的コンテンツを公開する場合、nginxやapacheを利用してWebサーバーを立てて公開する方法もありますが、クラウドネイティブな方法としてCloudFrontとS3を利用する方法が挙げられます。
これらを利用することにより、需要に応じたリソースのスケールや料金負担が実現できるほか、サーバー(インフラ)のメンテナンスを自身で行う必要がなくなるなど、コンテンツ配信の事業者の方々にとっても大きなメリットがあります。
一方、コンテンツの中には、特定のユーザーに公開を絞りたいケースもあるかと思います。
今回は、一つのサイト(ドメイン)の中で、全体に公開したいコンテンツと、公開を限定したいコンテンツを制御したいという事例を考えてみます。
使うもの
配信基盤として用いるものはAmazon CloudFrontとAmazon S3です。
また、認証にはAmazon Cognitoを利用し、CloudFrontと認証機能を連携させる手段としてLambda@Edgeを利用します。
具体的なLambda@Edge内の処理プログラムについては、AWS (AWS Labs)より提供されているcognito-at-edgeを利用します。
事前準備・確認
まずは、特定のパスに限定させず、全てのコンテンツに対して公開を制限したいと思います。
コンテンツ格納用バケットの用意
コンテンツを格納するためのS3バケットを用意します。今回は東京リージョンにデプロイしたいと思います。
バケット名以外の設定はデフォルトで問題ありません。(今回は「202509-web-contents-bucket」を作成しました。)
作成が終わったら、何かしらコンテンツを格納しておきましょう。
こういうときに生成AIを使うとサクッと作れていいですね。
Cognitoのデプロイ
Cognitoをデプロイします。今回は東京リージョンにデプロイしたいと思います。
CloudFormationより、以下のテンプレートをデプロイします。
パラメータのCognitoCallbackURLについては、ひとまずこのままでもOKです。
AWSTemplateFormatVersion: 2010-09-09
Description: Create Cognito. Please deploy at ap-northeast-1 region.
# Parameters
Parameters:
## CognitoコールバックURL
CognitoCallbackURL:
### cognito-at-edgeの仕様上、最後は"/"をつけない
Description: Cognito callback URL
Type: String
Default: https://example.com
# Resources
Resources:
# Cognito関連リソース
## Cognito User Pool
CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: pathauth-cognitouserpool
UserPoolTier: PLUS ### Cognitoのプラン
DeletionProtection: INACTIVE ### 検証のため(削除できるように)
UserPoolTags:
Name: pathauth-cognitouserpool
## Cognito User Pool Client
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName: pathauth-cognitouserpool-client
UserPoolId: !Ref CognitoUserPool
GenerateSecret: false
AllowedOAuthFlowsUserPoolClient: true
AllowedOAuthScopes:
- openid
AllowedOAuthFlows:
- code
CallbackURLs:
- !Ref CognitoCallbackURL
SupportedIdentityProviders:
- COGNITO
ExplicitAuthFlows:
- ALLOW_USER_SRP_AUTH
## Cognito User Pool Domain
CognitoUserPoolDomain:
Type: AWS::Cognito::UserPoolDomain
Properties:
UserPoolId: !Ref CognitoUserPool
Domain: pathauth-userpool-domain
ManagedLoginVersion: 2
## Cognito Managed Login Page
CognitoManagedLogin:
Type: AWS::Cognito::ManagedLoginBranding
Properties:
UserPoolId: !Ref CognitoUserPool
ClientId: !Ref CognitoUserPoolClient
UseCognitoProvidedValues: true
# Outputs
Outputs:
CognitoUserPoolId:
Description: Cognito User Pool ID
Value: !Ref CognitoUserPool
CognitoUserPoolClientId:
Description: Cognito User Pool Client ID
Value: !Ref CognitoUserPoolClient
CognitoUserPoolDomain:
Description: Cognito User Pool Domain
Value: !Sub "pathauth-userpool-domain.auth.${AWS::Region}.amazoncognito.com"
Lambda@Edgeのコード準備
続いて、Lambda@Edgeにデプロイするためのcognito-at-edgeを準備します。
事前にバージニア北部リージョンに、完成したコードを格納するためのバケットを用意しておきます。バケット名以外の設定はデフォルトで問題ありません。(今回は「202509-cognito-at-edge-bucket」を作成しました。)
続いて、任意のコードエディタなどで、以下のファイルを作成し、「index.js」という名前で保存します。
userPoolId、userPoolAppId、userPoolDomainについては、先ほど作成したスタックの出力を参照してください。
const { Authenticator } = require('cognito-at-edge');
const authenticator = new Authenticator({
// Replace these parameter values with those of your own environment
region: 'ap-northeast-1', // user pool region
userPoolId: 'ap-northeast-1_abcdefgh1', // user pool ID
userPoolAppId: '1examp1ec1ient1d', // user pool app client ID
userPoolDomain: 'pathauth-userpool-domain.auth.ap-northeast-1.amazoncognito.com', // user pool domain
});
exports.handler = async (request) => authenticator.handle(request);
作成が完了したらCloudShellで以下のコマンドを実行します。
[ ]内については適宜作業・ご自身の環境に置き換えてください。
[index.jsをアップロード] mkdir cognito-at-edge cd cognito-at-edge npm install cognito-at-edge mv ~/index.js ./ npx esbuild --bundle index.js --minify --outfile=bundle/index.js --platform=node cd bundle zip -r lambda-edge-auth.zip ./index.js aws s3 cp lambda-edge-auth.zip s3://[宛先S3バケット名]/lambda-edge-auth.zipさ
最終的にS3に「lambda-edge-auth.zip」というファイルがアップロードされていればOKです。
CloudFront・Lambda@Edgeのデプロイ
Cognitoと同じようにCloudFormationでデプロイします。
バージニア北部リージョンでデプロイするようにしてください。
パラメータについては、それぞれ作成したバケット名、Lambda@Edgeで用いるコードのファイル名(上記に従っていればlambda-edge-auth.zipになっているはずです。)を入力してください。
AWSTemplateFormatVersion: 2010-09-09
Description: Create CloudFront and Lambda@Edge. Please deploy at us-east-1 region.
Transform: AWS::Serverless-2016-10-31
# Mappings
Mappings:
# CachePolicyIdは以下の記事を参照
# https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html
CachePolicyIds:
# Recommended for S3
CachingOptimized:
Id: 658327ea-f89d-4fab-a63d-7e88639e58f6
# Parameters
Parameters:
# コンテンツの格納先(S3バケット名)
OriginS3Bucket:
Description: S3 bucket for contents
Type: String
Default: s3-bucket-name-for-contents
# Lambda@Edgeのコード格納先(S3バケット名)
LambdaEdgeCodeS3Bucket:
Description: S3 bucket for Lambda@Edge code
Type: String
Default: s3-bucket-name-for-cognito-at-edge
# Lambda@Edgeのコード格納先(S3キー名)
LambdaEdgeCodeS3Key:
Description: S3 key for Lambda@Edge code
Type: String
Default: lambda-edge-auth.zip
# Resources
Resources:
# CloudFront周辺リソース
## CloudFront Distribution
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- Id: "S3Origin"
DomainName: !Sub "${OriginS3Bucket}.s3.ap-northeast-1.amazonaws.com"
S3OriginConfig:
OriginAccessIdentity: ""
OriginAccessControlId: !Ref CloudFrontOriginAccessControl
DefaultCacheBehavior:
TargetOriginId: "S3Origin"
ViewerProtocolPolicy: redirect-to-https
# Recommended for S3
CachePolicyId: !FindInMap [CachePolicyIds, CachingOptimized, Id]
# cognito-at-edge
LambdaFunctionAssociations:
- EventType: viewer-request
LambdaFunctionARN: !Ref LambdaEdgeFunctionForAuth.Version
Enabled: true
IPV6Enabled: false
Tags:
- Key: Name
Value: !Sub "pathauth-cloudfront"
## CloudFront OriginAccessControl
CloudFrontOriginAccessControl:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Description: "Origin Access Control For S3 Static Website Hosting"
Name: !Sub "pathauth-s3oac"
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
# Lambda@Edge周辺リソース
## Lambda@Edge Function for authentication
LambdaEdgeFunctionForAuth:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub "pathauth-lambdaedge-auth"
AutoPublishAlias: pathauth
Handler: index.handler
Runtime: nodejs22.x
MemorySize: 128
Timeout: 5
CodeUri:
Bucket: !Ref LambdaEdgeCodeS3Bucket
Key: !Ref LambdaEdgeCodeS3Key
Role: !GetAtt RoleForLambdaEdge.Arn
## CloudWatch Log Group for Lambda@Edge
### 実際の実行ログは各エッジロケーションに作成される
LogGroupForLambdaEdge:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "/aws/lambda/${LambdaEdgeFunctionForAuth}"
RetentionInDays: 30
## IAM Role for Lambda@Edge
RoleForLambdaEdge:
Type: AWS::IAM::Role
Properties:
RoleName: cognito-at-edge-role
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
- edgelambda.amazonaws.com
Action: sts:AssumeRole
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- !Ref PolicyForLambdaEdge
## IAM Policy for Lambda@Edge
PolicyForLambdaEdge:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: cognito-at-edge-policy
Description: Policy for Lambda@Edge Function
Path: /
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- iam:CreateServiceLinkedRole
Resource: "*"
- Effect: Allow
Action:
- lambda:GetFunction
- lambda:EnableReplication
Resource:
### Allow access to all Lambda functions in the account at the specified region(us-east-1)
- !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:*:*"
- Effect: Allow
Action:
- cloudfront:UpdateDistribution
Resource:
### Allow access to all CloudFront distributions in the account
- !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/*"
コンテンツバケットのバケットポリシー設定
コンテンツバケットにCloudFrontからのアクセス(OAC)を許可します。
まずは、CloudFrontの当該ディストリビューションを開き、「オリジン」タブを選択します。
「S3Origin」を選択し、「編集」を押下します。
画面中段にあるOACに関する設定項目から、「ポリシーをコピー」を押下し、その下にある「S3 バケットアクセス許可に移動」を押下します。
すると、コンテンツバケットのページが開くので、「アクセス許可」からバケットポリシーを変更します。
CognitoのコールバックURL変更
今回は検証のため、CloudFrontのデフォルトドメイン(*.cloudfront.net)を使用します。
それに対応するよう、CognitoのコールバックURLを変更します。今回はコンソールより手動で変更します。
Cognitoのユーザープールを開いたら、「アプリケーションクライアント」から作成したアプリケーションクライアント(pathauth-cognitouserpool-client)を開きます。
次に、「ログインページ」タブを押下し、「マネージドログインページの設定」を編集します。
編集画面の一番上にある「許可されているコールバックURL」に、CloudFrontのURLを貼り付けます。
動作確認
では、きちんと認証機能が働くか確認をします。
任意のWebブラウザを開き、CloudFrontのドメイン名と、表示させたいコンテンツのパス(例:examp1edoma1n.cloudfront.net.index.html)を入力しアクセスします。
きちんとサインインページに遷移しましたね。実際にユーザー名などを入力しサインインすると…
コンテンツがしっかり表示されました。
検証
…ここからが本番です。
特定のパスのみ認証を設定していきます。今回は以下のようなフォルダ構成を仮定し、limited配下のみ公開に制限をかけます。
コンテンツバケット ├ limited/ │ ├ index.html │ └ style.css ├ index.html └ style.css
CloudFrontディストリビューションのビヘイビア編集
CloudFrontのコンソール画面から「ビヘイビア」タブを選択し、「ビヘイビアの作成」を押下します。
パスパターンに制限をかけたいパスにアスタリスクを追加(/limited*)、オリジンには宛先のS3オリジン(S3Origin)を選択し、他の箇所はデフォルトで「Create behavior」を押下します。
次に、デフォルトビヘイビア(*)を編集します。編集画面の一番下、「関数の関連付け」を「関連付けなし」に設定します。
CognitoのコールバックURL編集
CognitoのコールバックURLをパスつきのURLに変更します。
詳細な手順は事前準備のときとほとんど同じなので省略しますが、以下のようになっていればOKです。
Lambda@Edgeのコード編集
Lambda@Edgeのコードを特定のパスに対してのみ認証できるよう変更します。
先ほど作成したindex.jsに一部追記をします。
const { Authenticator } = require('cognito-at-edge');
const authenticator = new Authenticator({
// Replace these parameter values with those of your own environment
region: 'ap-northeast-1', // user pool region
userPoolId: 'ap-northeast-1_abcdefgh1', // user pool ID
userPoolAppId: '1examp1ec1ient1d', // user pool app client ID
userPoolDomain: 'pathauth-userpool-domain.auth.ap-northeast-1.amazoncognito.com', // user pool domain
parseAuthPath: '/limited', // 追記①
cookiePath: '/limited' // 追記②
});
exports.handler = async (request) => authenticator.handle(request);
追記が終わったら先程と同じようにCloudShellからzip化し、S3にアップロードします。名前は変えておくと分かりやすいかと思います。今回は「lambda-edge-auth-path.zip」としました。
Lambda@Edgeの更新
作成したコードでLambda@Edgeを更新します。今回はコンソールより更新します。
まず、先程アップロードしたS3バケットから、当該コンテンツのオブジェクトURLをコピーします。
次にLambdaのコンソールから、認証に用いているLambda(pathauth-lambdaedge-auth)を探し開きます。
コード編集画面の右上にある「アップロード元」から「Amazon S3の場所」を選択し、先程コピーしたURLを貼り付けます。
関数の更新が完了したら、Lambda@Edgeにデプロイします。
右上の「アクション」から「Lambda@Edgeへのデプロイ」を選択します。
ディストリビューションは作成したもの、キャッシュ動作には先程作成したビヘイビア(/limited*)を選択します。CloudFrontイベントについては「ビューアーリクエスト」に変更してください。
最後に一番下の「Lambda@Edge へのデプロイを確認」に✅️を入れてデプロイをします。
以上で変更作業は完了です。
動作確認
早速想定の挙動になるか見ていきます。
まずは、公開されているコンテンツ(ルートのindex.html)にアクセスします。
問題なく表示されました。認証もありません。
では、次に限定コンテンツ(/limited/index.html)にアクセスします。
きちんと認証画面に移りましたね!良かったです。認証情報を入れてサインインしてみると…
いかにも特別感のあるページが表示されました。ありがとう、チャッピー。
終わりに
事前準備が少し多かったので長くなってしまいましたが、パスごとの認証は思ったよりもシンプルな手順でできたので良かったです。
本記事の内容が、どなたかの参考になることを願っております。
余談ですが、先日生まれて初めてオイルマッサージなるものを体験してきました。
担当の方に「全身ボロボロですね…」と遠回しに言われ少し傷ついたので、折を見て通おうかなと思ったりしています。




















