デモアプリの Cognito 認証(おすすめ構成)
SkillCommunicationRecommended Cognito authentication setup for demo and web apps. How to wire up two sign-in paths, "Continue with Google" or "Email + passkey (password optional)", in CDK and the frontend, plus pitfalls of passkey-breaking configurations, where to put the Google client secret, login screen UX conve
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the デモアプリの Cognito 認証(おすすめ構成) skill
What this skill tells your AI
The instructions your AI receives, as published by minorun365/agent-builder-skills in skills/kb-demo-app-auth/SKILL.md and read by ahel’s review.
デモアプリ・Webアプリの認証は、特に指定がなければこの構成にする:「Googleで続ける」か「メール+パスキー」。
動く実物は minorun365/marp-agent。
迷ったら実物を読む。設計の背景は同リポジトリの docs/authentication-options.md。
| ファイル | 中身 |
|---|---|
infra/lib/auth-stack.ts | User Pool / App Client / Google IdP / Cognito ドメイン |
src/components/Auth/AuthScreen.tsx | ログイン画面(326行。移植元) |
src/components/Auth/AuthScreen.css | 同スタイル |
infra/lambda/auth/google-idp-manager/handler.ts | Google IdP を作るカスタムリソース |
infra/lambda/auth/google-link/handler.ts | preSignUp。既存メールユーザーへGoogleを連携 |
1. 利用者から見た形
| 経路 | 初回 | 2回目以降 |
|---|---|---|
| 「Googleで続ける」で登録とログインが同時に終わる | ||
| メール | メールアドレス+パスワードで登録し、確認コードでメールを確認 | パスキー(顔・指紋)。パスワードも使える |
- メールのワンタイムコードログインは採用しない。 SES の本番利用申請を認証の前提にしない。Cognito 標準メールは新規登録の確認とパスワード再設定だけに使う(AWSアカウントあたり1日50通)。
- パスキーはパスワードの置き換えではない。 登録しなくても期限なくパスワードで使える。
- Google 利用者へパスキー登録を案内しない。 本人確認は Google 側が担当している。
2. ログイン画面のUX規約
1画面目は 2択だけに絞る。「Googleで続ける」と、メールアドレス入力+「メールで続ける」。
メールを入れた次の画面では、パスキーとパスワードを常に並べて出す。
⚠️ パスキー登録の有無で画面を出し分けない。 出し分けると「そのメールアドレスが登録済みか」を第三者へ教えることになる。同じ理由で、存在しないアドレスも認証失敗も表示は「メールアドレスまたは認証情報を確認してください。」に統一し、Cognito 側も
preventUserExistenceErrors: trueにする。
パスワードでログインした利用者には、成功後に一度だけパスキー登録を案内する。
- 「パスキーを登録」と「あとで」の2つだけ
- 「あとで」を押されたら 30日間は再案内しない(
localStorageに押した時刻を持つ) - 専用の設定画面は作らない。ログイン後のメイン画面も変えない
3. CDK(User Pool)
パスキーは設定が1つでも欠けるとエラーにならず、ただ使えない形で失敗する。次の5点をまとめて入れる。
this.userPool = new cognito.UserPool(this, 'UserPool', {
featurePlan: cognito.FeaturePlan.ESSENTIALS, // ① LITE ではパスキーを使えない
mfa: cognito.Mfa.OFF, // ② 明示しないとOPTIONALへ補完されることがある
selfSignUpEnabled: true,
signInAliases: { email: true },
signInCaseSensitive: false,
autoVerify: { email: true },
standardAttributes: { email: { required: true, mutable: true } },
accountRecovery: cognito.AccountRecovery.EMAIL_ONLY,
signInPolicy: {
allowedFirstAuthFactors: { password: true, passkey: true }, // ③ パスキーを第1認証要素に
},
passkeyRelyingPartyId: props.appDomain, // ④ 配信ドメインと完全一致させる
passkeyUserVerification: cognito.PasskeyUserVerification.PREFERRED,
passwordPolicy: { minLength: 8, requireDigits: false, requireLowercase: false, requireSymbols: false, requireUppercase: false },
});
this.userPoolClient = this.userPool.addClient('WebClient', {
generateSecret: false,
preventUserExistenceErrors: true,
authFlows: { user: true, userPassword: true, userSrp: true }, // ⑤ user:true = USER_AUTH。無いとパスキーを選べない
accessTokenValidity: cdk.Duration.minutes(60),
idTokenValidity: cdk.Duration.minutes(60),
refreshTokenValidity: cdk.Duration.days(30),
});
- ④ の RP ID は「利用者がアクセスするドメイン」。
dxxxx.cloudfront.netのような配信基盤の既定ドメインでもよいが、後からカスタムドメインへ移すと登録済みのパスキーは全部使えなくなる(RP ID が変わるため)。カスタムドメインを付ける予定があるなら、パスキーを入れる前に先に付ける。 - パスワードポリシーを厳しくしない。 パスキーを入れる目的は入力を減らすこと。記号必須・12文字のような設定はデモの初回登録を重くするだけ。
featurePlan: ESSENTIALSは MAU 課金が発生する。デモ規模なら無視できるが、LITE からの変更なので認識しておく。
フロント側(aws-amplify v6)
// パスキーでログイン
await signIn({ username: email, options: { authFlowType: 'USER_AUTH', preferredChallenge: 'WEB_AUTHN' } });
// パスワードでログイン
await signIn({ username: email, password, options: { authFlowType: 'USER_PASSWORD_AUTH' } });
// ログイン後にパスキーを登録
await associateWebAuthnCredential();
Amplify.configure の Auth.Cognito に userPoolId / userPoolClientId を渡すだけでよい。Google を使うときだけ loginWith.oauth を足す(次節)。
4. Google ログイン
OAuth クライアントはデモごとに作らず、共通の1つを使い回す
同意画面のアプリ名は GCP プロジェクト単位で1つなので、デモごとにクライアントを作っても名前は分けられない。 デモ用の中立な名前でプロジェクトとクライアントを1つ用意し、以後のデモは全部これを使い回す。
| 項目 | 決め方 |
|---|---|
| GCP プロジェクト | デモ専用に1つ作る |
| 同意画面のアプリ名 | 利用者に見えるのはこれ。特定のデモ名にせず、中立な名前にする |
| 公開ステータス | 本番環境(テストユーザーの登録なしで誰でもログインできる) |
| Client ID | CDK のコンテキストで渡す(秘密ではない) |
| Client Secret | デプロイ先の AWS アカウントへ置く(下の「client secret の置き場」) |
新しいデモを足すときの作業は2つだけ。
- Google Cloud Console でそのクライアントを開き、承認済みリダイレクト URI に新しい Cognito ドメインの
/oauth2/idpresponseを1行足す - そのデモの AWS アカウントへ Secret を入れ、CDK に
googleClientIdを渡す
⚠️ 組織の Google Workspace 配下の GCP プロジェクトは、同意画面が「内部」(
orgInternalOnly)になっていることがある。 その場合は組織のアカウントしかログインできず、外部の人が触るデモでは機能しない。プロジェクトを選ぶ前に同意画面のユーザータイプを確かめる。⚠️
gcloudでも API でも、クライアントの作成も編集もできない。リダイレクトURIの追記も含めて Console のブラウザ操作が唯一の手段(2026-08 時点)。根拠は3つ:
gcloud alpha iap oauth-clientsは IAP 専用で、ヘルプ自身が「プロジェクト内の全 OAuth クライアントの管理 API としては使えない」と明記。加えて IAP OAuth Admin API は 2026-03-19 に完全停止iap v1の discovery を見るとprojects.brands.identityAwareProxyClientsの method はcreate/get/list/delete/resetSecretだけで、更新系が無い。停止していなくてもURIは足せない- Google の公開 API 一覧(
https://www.googleapis.com/discovery/v1/apis)に、OAuth クライアントを管理する API 自体が存在しない人に操作を頼むときは、クライアントの編集画面まで開けるURLを渡す。 URLはこの形:
https://console.cloud.google.com/auth/clients/<クライアントID>?project=<プロジェクトID>
実装に必要なもの
Cognito ドメイン(Hosted UI 用)、Google の OAuth クライアント ID / Secret、IdP 登録、App Client の OAuth 設定。
supportedIdentityProviders: [cognito.UserPoolClientIdentityProvider.COGNITO, cognito.UserPoolClientIdentityProvider.GOOGLE],
oAuth: {
flows: { authorizationCodeGrant: true },
scopes: [cognito.OAuthScope.OPENID, cognito.OAuthScope.EMAIL, cognito.OAuthScope.PROFILE],
callbackUrls: [`https://${appDomain}/`, 'http://localhost:5173/'],
logoutUrls: [`https://${appDomain}/`, 'http://localhost:5173/'],
},
フロントは signInWithRedirect({ provider: 'Google' }) を呼ぶだけ。
⚠️ Amplify Gen2 の defineAuth では「スコープ」が2か所にあり、書き方が逆になる。 ここを取り違えると
デプロイも構成検査も通ったうえで、Googleのログイン画面が invalid_scope で開かない。
| 場所 | 何のスコープか | 書き方 |
|---|---|---|
externalProviders.scopes | Cognito のアプリクライアントが出すトークンの範囲 | 大文字の列挙(['EMAIL','PROFILE','OPENID']) |
externalProviders.google.scopes | Google へそのまま渡る生の文字列 | 小文字(['openid','email','profile']) |
後者に大文字を書くと authorize_scopes が "EMAIL PROFILE OPENID" として IdP に入り、Google が
invalid=[OPENID] を返す(EMAIL / PROFILE は通ってしまうので、症状が OPENID だけに出て気づきにくい)。
この種の失敗は AWS 側をいくら見ても分からない。 デプロイ後に Cognito の認可エンドポイントへ
リクエストを投げ、accounts.google.com のログイン画面へ着地するかを確かめる(サインインは不要)。
エラー時は authError クエリが base64 で理由を持っている:
curl -s -o /dev/null -L -w '%{url_effective}\n' \
"https://<Cognitoドメイン>.auth.<region>.amazoncognito.com/oauth2/authorize?identity_provider=Google&client_id=<アプリクライアントID>&response_type=code&scope=openid+email+profile&redirect_uri=https%3A%2F%2F<配信ドメイン>%2F"
.../signin/oauth/error?authError=... へ着地したら失敗。authError を base64url デコードすると
invalid_scope / redirect_uri_mismatch などの理由が平文で読める。
client secret の置き場
cognito.UserPoolIdentityProviderGoogle の L2 は secret を CloudFormation テンプレートへ平文で残す。marp-agent は、秘密値を state に残さないカスタムリソース(google-idp-manager)で IdP を作る形にしている。
⚠️ 組織の管理下にある AWS アカウントでは、SCP で
secretsmanager:CreateSecretが拒否されていることがある。 marp-agent の handler は Secrets Manager から読む実装なので、その場合は SSM Parameter Store の SecureString に置き換える。Lambda 実行ロールにssm:GetParameterと、SecureString を復号するkms:Decryptを付ける。
段階的に入れてよい
Google は GCP 側の作業(OAuth クライアント作成、承認済みリダイレクトURIの追加)が要るので、「クライアントIDが渡されたときだけ Google を有効にする」実装にしておくと、パスキーだけ先にリリースできる。marp-agent は this.node.tryGetContext('googleClientId') の有無で分岐している。
同じメールアドレスの重複プロフィール
Cognito は、メールアドレスが同じでもローカルユーザーと Google ユーザーを自動で統合しない。放置すると同じ人に2つのプロフィールができる。marp-agent は preSignUp トリガー(google-link)で、確認済みメールアドレスが一致する既存プロフィールへ AdminLinkProviderForUser でリンクしている。デモアプリで既存利用者がいないなら、初期は省いてよい。
5. デモアプリでの判断(外部の人が触る場合)
- Google の同意画面に出るアプリ名は GCP プロジェクトのブランディングで決まる。クライアントIDを別に作っても、同じプロジェクトなら名前は同じ。外部の人に見せるデモで、無関係な別アプリの名前が出る状態にしない。
- 管理を楽にするなら、デモ共通の中立な名前でプロジェクトとクライアントを1つ用意し、以後のデモはリダイレクトURIを1行足すだけにする。デモごとに作らない。
- セキュリティ要件の厳しい組織の利用者は、業務用 Google アカウントを外部アプリへ繋ぐことに抵抗がある。Google を出しても、メール経路を同格で並べる(片方だけにしない)。
6. 検証(実装完了と言う前に)
パスキーは「画面が出た」では動作確認にならない。次を実操作で通す。
- メールで新規登録 → 確認コード → ログイン
- パスキー登録 → いったんサインアウト → パスキーでログイン
- パスキー未登録の状態でパスキーを押し、共通エラーが出てパスワードへ戻れる
- Google ログイン(有効にした場合)
- PC と iPhone の両方(iOS Simulator か実機の Safari)
localhost は RP ID が localhost になるため、本番ドメインで登録したパスキーはローカルでは使えない。ローカルではパスワード経路で確認する。
Signals
- GitHub stars
- 23
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
kb-demo-app-auth- Source
- github.com/minorun365/agent-builder-skills