AWS CDK (bun) で Cognito 認証基盤を作る — IAM 設計からデプロイまで

Posted on:2026-05-28

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 のデプロイまでが第一段階。ここからアプリ側に繋いでいく。