はじめに
Google Workspace(以下GWS)をIdPとしたSAML認証で、Amazon Cognitoの認証基盤を構築し、さらにDev環境へのアクセス制御を「ALB認証アクション」と「CloudFront + Lambda@Edge」の2方式で実装・比較した記録です。
インフラ初心者が実際に手を動かしながら詰まったポイントも含めて残しているので、同じ構成を組む方の参考になれば幸いです。
この記事で扱うこと
- Amazon Cognito User Poolの作成
- GWSをIdPとしたSAML 2.0連携
- ALB認証アクションによるインフラレイヤのアクセス制御
- CloudFront + Lambda@Edge(
cognito-at-edge)によるアクセス制御 - 2方式のメリット・デメリット比較
想定構成
ユーザー → CloudFront → ALB(internal) → ECS(Frontend / Backend)
用語整理
最初に登場人物を整理しておきます。
| 用語 | 役割 | 例え |
|---|---|---|
| Google Workspace (IdP) | 「この人は本人です」と証明する側 | パスポート発行所 |
| Amazon Cognito (SP) | 証明を受け取りトークンを発行する側 | 入国審査 |
| SAML 2.0 | IdPとSPの間の通信ルール | パスポートの規格 |
Part 1. Amazon Cognito User Poolの作成
1-1. User Poolを作成する
AWSマネジメントコンソールからCognitoを開き、User Poolを作成します。新しいUIではウィザードではなく1画面に統合されています。
| 項目 | 設定値 |
|---|---|
| アプリケーションタイプ | 従来のウェブアプリケーション |
| サインイン識別子 | メールアドレス |
| 自己登録 | 無効(チェックを外す) |
| 必須属性 | メールアドレス |
ポイント
認証はGWSに委譲するため、Cognito側で自己登録を有効にする必要はありません。「従来のウェブアプリケーション」を選ぶと、バックエンドでの認可コードフローに適した設定になります。
1-2. ドメインを確認する
作成後、アプリケーションの統合 > ドメイン でCognitoドメイン(またはカスタムドメイン)を確認します。このドメインがSAMLのACS URLのベースになります。
例: https://xxxxx.auth.ap-northeast-1.amazoncognito.com
この時点で、SAML連携に必要な以下2つの値が決まります。
| 値 | 形式 |
|---|---|
| Entity ID | urn:amazon:cognito:sp:<User Pool ID> |
| ACS URL | https://<ドメイン>/saml2/idpresponse |
Part 2. Google WorkspaceでSAMLアプリを作成する
2-1. カスタムSAMLアプリを追加
Google Admin Console(admin.google.com)に特権管理者でログインし、以下の手順で進めます。
アプリ > ウェブアプリとモバイルアプリアプリを追加 > カスタムSAMLアプリを追加- アプリ名を入力
2-2. GoogleのIdP情報をダウンロード
「Google IdP情報」画面で メタデータ(GoogleIDPMetadata.xml)をダウンロード します。このXMLにEntity ID・SSO URL・X.509証明書がすべて含まれています。
<md:EntityDescriptor entityID="https://accounts.google.com/o/saml2?idpid=XXXX" validUntil="2031-02-07T...">
...
<md:SingleSignOnService Location="https://accounts.google.com/o/saml2/idp?idpid=XXXX"/>
</md:EntityDescriptor>
| 項目 | XML内の場所 |
|---|---|
| Entity ID |
entityID 属性 |
| SSO URL |
SingleSignOnService の Location
|
| X.509証明書 |
X509Certificate 要素 |
| 証明書有効期限 |
validUntil 属性 |
運用メモ
validUntilの値は証明書の有効期限です。運用ドキュメントに記録しておき、期限前に更新できるようにしておきましょう。
2-3. サービスプロバイダの詳細を入力
Cognito側の値を入力します。
| 項目 | 値 |
|---|---|
| ACS URL | https://<ドメイン>/saml2/idpresponse |
| Entity ID | urn:amazon:cognito:sp:<User Pool ID> |
| Name IDフォーマット | |
| Name ID | Primary email |
ハマりどころ
ACS URLとEntity IDは完全一致が必要です。スペルミスや末尾スラッシュの有無で認証が失敗します。
2-4. 属性マッピング
| Google Directory属性 | アプリ属性 |
|---|---|
| Primary email | |
| First name | firstName |
| Last name | lastName |
2-5. アプリを有効化
作成したアプリはデフォルトで無効です。ユーザーアクセス > 全員に対してオン に変更しないとログインテストができません。
Part 3. CognitoにSAML IdPを登録する
3-1. IDプロバイダーを追加
サインインエクスペリエンス > フェデレーテッドIDプロバイダー > IDプロバイダーを追加 でSAMLを選択し、2-2でダウンロードした GoogleIDPMetadata.xml をアップロードします。
3-2. 属性マッピング
Cognito側は「ユーザープール属性(左)」と「SAML属性(右)」の対応を設定します。SAML属性側はGWSで設定した属性名を手入力します。
| ユーザープール属性 | SAML属性 |
|---|---|
| given_name | firstName |
| family_name | lastName |
3-3. アプリクライアントへ紐付け
アプリクライアントの「ログインページ」設定で、IDプロバイダーに先ほど登録したSAML IdPを指定します。
| 項目 | 値 |
|---|---|
| IDプロバイダー | GoogleWorkspace |
| OAuth 2.0許可タイプ | 認証コード付与 |
| OpenID Connectスコープ | openid, email, profile |
3-4. 動作確認
Hosted UI(マネージドログイン)からテストし、Googleログイン画面 → ログイン → コールバックURLに ?code=xxxx が返ればSAML連携は成功です。
Part 4. ALB認証アクションでアクセス制御する
ここからはインフラレイヤでのアクセス制御です。アプリのコードを変更せず、ALBの機能だけでCognito認証を強制します。
4-1. ALB用アプリクライアントを作成
重要
ALBのCognito認証は クライアントシークレットが必須 です。シークレットなしのクライアントを指定すると以下のエラーになります。InvalidLoadBalancerActionException: The user pool client must have a client secret
シークレットありのアプリクライアントを新規作成し、コールバックURLに以下を追加します。
https://<ALBドメイン>/oauth2/idpresponse
4-2. リスナールールに認証アクションを追加
HTTPS:443のリスナールールに「ユーザーの認証」アクションを追加します。アクションの順番が重要です。
| 順番 | アクション |
|---|---|
| 1番目 | ユーザーの認証(Cognito) |
| 2番目 | ターゲットグループへ転送 |
ヘルスチェックの注意
/healthなどのヘルスチェックパスに認証をかけるとヘルスチェックが失敗します。優先度の高いルールで/healthを認証なしに分離しておきましょう。
最終的なルール構成例:
| 優先度 | 条件 | 認証 | 転送先 |
|---|---|---|---|
| 50 | /health |
なし | backend |
| 100 | /api/* |
あり | backend |
| デフォルト | 上記以外 | あり | frontend |
4-3. CloudFront経由でのハマりどころ
CloudFront → ALB構成の場合、いくつか追加対応が必要でした。
リダイレクトループ
CloudFrontのオリジンリクエストポリシーが AllViewerExceptHostHeader だと、Hostヘッダーが転送されずリダイレクトループになります。AllViewer に変更し、CloudFrontドメインのコールバックURL(https://<CloudFrontドメイン>/oauth2/idpresponse)もCognitoに追加します。
500 Internal Server Error
ALBが認証コードをトークンに交換する際、Cognitoのトークンエンドポイントへ通信します。ALBのセキュリティグループのアウトバウンドでHTTPS(443)が許可されていないと、この通信が失敗して500エラーになります。アウトバウンドルールを確認しましょう。
Part 5. CloudFront + Lambda@Edgeでアクセス制御する
もう一つの方式として、CloudFrontのエッジで認証する方法を試します。cognito-at-edge というライブラリを使います。
5-1. クライアントシークレットの考え方
cognito-at-edge は クライアントシークレットなし で動作させられます。
| 方式 | シークレット | 理由 |
|---|---|---|
| ALB認証 | 必須 | AWSの仕様。ALBがマネージドで安全に保持できる前提 |
| Lambda@Edge | 不要にできる | Lambda@Edgeは環境変数が使えずコード直書きになるため、シークレットを含めない方がむしろ安全 |
5-2. Lambda関数のコード
const { Authenticator } = require('cognito-at-edge');
const authenticator = new Authenticator({
region: 'ap-northeast-1',
userPoolId: 'ap-northeast-1_xxxxxxxxx',
userPoolAppId: 'xxxxxxxxxxxxxxxxxxxxxxxxxx',
userPoolDomain: 'auth.dev.example.net',
});
exports.handler = async (request) => authenticator.handle(request);
依存パッケージを含めてzip化し、Lambdaにアップロードします。
npm install cognito-at-edge
zip -r function.zip index.js node_modules/
5-3. Lambda@Edgeデプロイの注意点
us-east-1で作成する
Lambda@Edge関数は 必ず us-east-1(バージニア北部) で作成する必要があります。
信頼ポリシーの修正
実行ロールの信頼ポリシーに edgelambda.amazonaws.com を追加します。
"Service": [
"lambda.amazonaws.com",
"edgelambda.amazonaws.com"
]
バージョン発行が必須
Lambda@Edgeは $LATEST を使えません。バージョンを発行し、発行済みバージョンをCloudFrontに関連付けます。これはエッジロケーションにコードを配置する都合上、コードを確定させる必要があるためです。
VPCオリジンの制約
CloudFrontがVPCオリジン(内部ALBへ直接接続する構成)を使っている場合、オリジンリクエストではLambda@Edgeを関連付けられません。
trigger の作成中にエラーが発生しました:
VPC origin with an origin Lambda@Edge association is not supported.
cognito-at-edge はビューワーリクエストで認証する設計なので、ビューワーリクエストで関連付ければ問題ありません。
| CloudFrontイベント | 設定 |
|---|---|
| ビューワーリクエスト | ◯(これを使う) |
| オリジンリクエスト | ✕(VPCオリジンでは不可) |
Part 6. ALB認証 vs Lambda@Edge 比較
| 観点 | ALB認証アクション | CloudFront + Lambda@Edge |
|---|---|---|
| 実装の手間 | 少ない(GUI設定のみ、コード不要) | 多い(Lambda保守が必要) |
| 認証を止める位置 | ALB(リージョン) | エッジ(CloudFront) |
| クライアントシークレット | 必須 | 不要にできる |
| CloudFrontキャッシュ | 効きにくい(毎回ALBへ) | 効かせられる(エッジで認証完結) |
| デプロイの制約 | 通常のALB設定 | us-east-1・バージョン発行・反映に数分 |
| デバッグ | リージョンにログ集約で見やすい | エッジに分散して見づらい |
キャッシュについての補足
「ALB認証だとCloudFrontのオリジンキャッシュが効かない」という指摘はその通りですが、キャッシュポリシーが CachingDisabled の場合はそもそもキャッシュしていないため、この差は問題になりません。将来的にキャッシュを積極利用する予定があるかどうかが判断の分かれ目です。
どちらを選ぶか
- シンプルさ重視・キャッシュ予定なし → ALB認証アクション
- 将来的にCloudFrontキャッシュを活用したい → Lambda@Edge
Dev環境であれば、まずはシンプルなALB認証アクションで十分なケースが多いと考えます。
まとめ
- GWSをIdPとしたSAML連携は、
GoogleIDPMetadata.xmlをCognitoにアップロードするだけで証明書・SSO URL・Entity IDがまとめて登録できる - インフラレイヤのアクセス制御は「ALB認証アクション」と「Lambda@Edge」の2方式がある
- ALB認証はシンプルだがキャッシュが効きにくく、シークレット必須
- Lambda@Edgeはキャッシュを活かせるが、us-east-1・バージョン発行・VPCオリジン制約などの考慮が必要
同じ構成で詰まった方の助けになれば幸いです。