AWSの認証・認可基盤を自分の手で触って理解したい、という目的で勉強を始めた。まずは Amazon Cognito を使い、AWS CDK (TypeScript) と bun で IaC として管理できる構成まで作った。
環境の再現性を担保しつつ、IAM の設計から Cognito のリソース定義、デプロイまでの流れをメモとして残す。同じ手順をまたやるときの備忘録にもなる。
はじめに
ツール選定は以下の通り。
- IaC: AWS CDK (TypeScript)
- パッケージマネージャー: bun
- リージョン: ap-northeast-1(東京)
CDK でコード管理することで、cdk destroy で学習用リソースを片付けられる。dev 環境では removalPolicy: DESTROY にしておき、試行錯誤のコストを下げている。
IAM 設計
アカウント体制
root アカウントを日常的な CLI 操作に使うのは危険なので、用途別に IAM ユーザーを分けた。
| アカウント | 用途 | 権限 | ログイン方法 |
|---|---|---|---|
| root | 課金・初期設定のみ | 全権限 | メール+パスワード(MFA必須) |
| cdk-deploy | CDK/CLI操作 | AdministratorAccess | アクセスキーのみ |
| yourname-console | コンソール確認 | AdministratorAccess | ユーザー名+パスワード |
cdk-deploy はコンソールログイン不要。アクセスキーのみ発行して CLI から使う。
学習用・個人開発では AdministratorAccess で問題ない。本番・チーム開発では最小権限に絞るのが次の課題になる。
グループ経由でポリシーを管理する
ユーザーに直接ポリシーをアタッチするのではなく、グループ経由で管理するのがベストプラクティス。
AdminGroup → AdministratorAccess
└── cdk-deploy
└── yourname-console
ユーザーが増えたときやポリシーを変更したいとき、グループを操作するだけで済む。
アクセスキーの設定
aws configure --profile cdk-dev
AWS Access Key ID: AKIA...
AWS Secret Access Key: xxxxxx
Default region name: ap-northeast-1
Default output format: json
設定確認:
aws sts get-caller-identity --profile cdk-dev
プロファイルの切り替えは --profile オプションか環境変数で行う。
export AWS_PROFILE=cdk-dev
プロジェクト構成
auth-sample-cognito/
└── infra/
├── bin/
│ └── app.ts # CDK エントリポイント
├── lib/
│ └── cognito-auth-stack.ts # Cognito リソース定義
├── cdk.json
├── package.json
└── tsconfig.json
CDK の設定ファイル
cdk.json
bun を使う場合、app の指定を bun run にするだけで ts-node が不要になる。bun はネイティブで TypeScript を実行できるので、トランスパイルのステップが省略できる。
{
"app": "bun run bin/app.ts"
}
package.json
{
"dependencies": {
"aws-cdk-lib": "^2.130.0",
"constructs": "^10.3.0"
},
"devDependencies": {
"aws-cdk": "^2.130.0",
"bun-types": "latest",
"typescript": "^5.3.0"
}
}
bun-types を追加することで型補完が有効になる。
bin/app.ts
source-map-support/register は bun では不要(ネイティブでソースマップ対応済み)。
import * as cdk from "aws-cdk-lib";
import { CognitoAuthStack } from "../lib/cognito-auth-stack";
const app = new cdk.App();
new CognitoAuthStack(app, "CognitoAuthStack", {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION ?? "ap-northeast-1",
},
appOrigin: app.node.tryGetContext("appOrigin") ?? "http://localhost:3000",
stage: app.node.tryGetContext("stage") ?? "dev",
});
Cognito スタックの定義
User Pool
this.userPool = new cognito.UserPool(this, "UserPool", {
userPoolName: `${stage}-user-pool`,
selfSignUpEnabled: true,
signInAliases: { email: true },
autoVerify: { email: true },
passwordPolicy: {
minLength: 8,
requireLowercase: true,
requireUppercase: true,
requireDigits: true,
requireSymbols: false,
},
accountRecovery: cognito.AccountRecovery.EMAIL_ONLY,
mfa: isProd ? cognito.Mfa.REQUIRED : cognito.Mfa.OPTIONAL,
mfaSecondFactor: { sms: false, otp: true },
removalPolicy: isProd ? cdk.RemovalPolicy.RETAIN : cdk.RemovalPolicy.DESTROY,
});
dev 環境では removalPolicy: DESTROY にしておくことで cdk destroy でリソースを全部消せるようにしている。
Hosted UI ドメイン
Cognito のドメインプレフィックスはグローバルでユニークである必要がある。固定文字列だと他のアカウントと衝突するリスクがあるため、アカウントIDをサフィックスに使う。
const domainPrefix = `${stage}-asc-${cdk.Aws.ACCOUNT_ID}`;
this.userPool.addDomain("CognitoDomain", {
cognitoDomain: { domainPrefix },
});
App Client (PKCE)
ブラウザ向けの App Client は generateSecret: false にして PKCE を使う構成にする。Client Secret をブラウザに持たせると漏洩リスクがあるため。
this.userPoolClient = this.userPool.addClient("WebAppClient", {
generateSecret: false,
oAuth: {
flows: {
authorizationCodeGrant: true,
implicitCodeGrant: false, // 非推奨なので無効
},
scopes: [
cognito.OAuthScope.OPENID,
cognito.OAuthScope.EMAIL,
cognito.OAuthScope.PROFILE,
],
callbackUrls: [`${appOrigin}/callback`, "http://localhost:3000/callback"],
logoutUrls: [`${appOrigin}/`, "http://localhost:3000/"],
},
accessTokenValidity: cdk.Duration.minutes(60),
refreshTokenValidity: cdk.Duration.days(30),
enableTokenRevocation: true,
});
ユーザーグループ (RBAC)
new cognito.CfnUserPoolGroup(this, "AdminGroup", {
userPoolId: this.userPool.userPoolId,
groupName: "admin",
precedence: 1,
});
cognito:groups クレームがアクセストークンに含まれるため、バックエンド側でロールベースのアクセス制御が可能になる。
デプロイ手順
Bootstrap(初回のみ)
CDK がデプロイに使うリソース(S3、IAMロールなど)を事前に作成する。アカウント × リージョンごとに1回だけ必要。
bunx cdk bootstrap --profile cdk-dev
デプロイ
bun install
bunx cdk diff -c stage=dev --profile cdk-dev
bunx cdk deploy -c stage=dev -c appOrigin=http://localhost:3000 --profile cdk-dev
成功すると Outputs に以下が表示される。
CognitoAuthStack.UserPoolId = ap-northeast-1_xxxxxxxxx
CognitoAuthStack.UserPoolClientId = xxxxxxxxxxxxxxxxxxxxxxxxxx
CognitoAuthStack.CognitoHostedUiUrl = https://dev-asc-xxxx.auth.ap-northeast-1.amazoncognito.com
CognitoAuthStack.JwksUri = https://cognito-idp.ap-northeast-1.amazonaws.com/.../.well-known/jwks.json
デプロイ後の値は SSM Parameter Store にも保存している(/myapp/${stage}/cognito/*)。フロントエンド・バックエンドの設定や JWT 検証で使う。
ハマりポイント
source-map-support は bun では不要
npx ts-node 向けのボイラープレートをそのままコピーすると bin/app.ts の先頭に以下が入っていることがある。
import "source-map-support/register"; // ← bun では不要、エラーになる
bun はネイティブでソースマップをサポートしているので削除する。
Cognito ドメインプレフィックスの衝突
固定文字列のドメインプレフィックスは他の AWS アカウントと衝突してデプロイが失敗する。cdk.Aws.ACCOUNT_ID をサフィックスに使うことで回避できる。
| エラー | 原因 | 対処 |
|---|---|---|
Cannot find module 'source-map-support' |
bun で不要な import が残っている | bin/app.ts から該当行を削除 |
Invalid request: AWS::Cognito::UserPoolDomain |
ドメインプレフィックスが衝突 | cdk.Aws.ACCOUNT_ID をサフィックスに追加 |
Specify an environment name |
cdk.json がないディレクトリで実行 |
infra/ ディレクトリに移動して実行 |
費用
Cognito は月間 50,000 MAU まで無料。学習用途では実質無料で使える。CDK Bootstrap で作られる S3 バケットはほぼ0円(数円/月レベル)。
学習が終わったら以下で全リソースを削除できる。
bunx cdk destroy -c stage=dev --profile cdk-dev
手順を Skill として残した
今回の作業は、ドキュメントとあわせて Cursor / Claude 用の Skill(aws-cdk-cognito)にもまとめた。
前回の記事で書いた通り、Skill が効くのは AI が知らないことを教えるとき だ。一般的な CDK や Cognito の説明はモデルがすでに持っている。一方で、次のような判断は試行錯誤の結果であり、毎回ゼロから説明するのは非効率になる。
- bun では
source-map-supportを入れない - Cognito のドメインプレフィックスに
cdk.Aws.ACCOUNT_IDを付ける - ブラウザ向け App Client は
generateSecret: false+ PKCE - dev は
removalPolicy: DESTROYにしておく
Skill には IAM 設計の原則、プロジェクト構成、cdk bootstrap / deploy / destroy のコマンド、よくあるエラー表を入れている。「CDK で Cognito を作りたい」「bun + CDK のセットアップ」といった話題のときに参照される想定だ。
Skillsを入れればAIは賢くなるわけではないという整理と重なるが、今回のように 自分がハマったポイントを型として固定する 用途には向いている。次にフロントエンド連携や JWT 検証に進むときも、同じように Skill を足していく予定だ。
次のステップ
- フロントエンドとの連携(PKCE フロー実装)
- バックエンドの JWT 検証ミドルウェア実装
- MCP サーバーとの認証統合
認証基盤の勉強は Cognito + CDK のデプロイまでが第一段階。ここからアプリ側に繋いでいく。