Cognito を Local で勉強してみた(Moto + LocalStack + API)

November 23, 2025

■ はじめに

「Cognito の認証フローを完全に理解したい」「実際にアプリに実装する前にローカルで動かしたい」という理由から、Cognito をローカルで“ほぼ本番同等”に動かす環境を構築してみました。 検証レベルならどれも無料枠で使える AWS サービスなのですが、ローカルでも動かせると便利そうなので試してみました。

使用した技術:

  • LocalStack(Cognito / Lambda などのローカル AWS)
  • Moto(Cognito の動作をより忠実にモック)
  • Express API(API 各種)
  • React + Vite(フロントエンド)
  • Mailhog(メール送信のモック)
  • httpOnly Cookie による Refresh Token ローテーション
  • Jest + supertest(E2E テスト)
  • dynamoDB 操作ログの保存

今回の学びは GitHub に公開しています。 ただ、なんとなく動作検証してみただけなので結構雑いです。

👉 https://github.com/h-neco/study-local-aws-cognito


環境構築

  • docker

localstack,moto,mailhog を立ち上げます。 localstack 無料プランでは、cognito は扱えないので、moto を利用します。 また、cognito によるメール送信を擬似的に再現するため、mailhog を利用します。

services:
    localstack:
        image: localstack/localstack
        container_name: localstack
        ports:
            - "4566:4566"
        environment:
            - SERVICES=s3,dynamodb,lambda
            - DEBUG=1
        volumes:
            - "./localstack:/var/lib/localstack"
        networks:
            - local-net

    moto:
        image: motoserver/moto:latest
        container_name: moto
        environment:
            - MOTO_BIND_HOST=0.0.0.0
            - MOTO_PORT=5000
            - MOTO_ALLOW_NONEXISTENT_SERVICE=True
        command: ["-H", "0.0.0.0", "-p", "5000"]
        ports:
            - "5001:5000"
        networks:
            - local-net

    mailhog:
        image: mailhog/mailhog
        ports:
            - "8025:8025" # Web UI
            - "1025:1025" # SMTP
        networks:
            - local-net

networks:
    local-net:
        driver: bridge
  • terraform

provider をローカルに向けて、endpoint を設定していきます。

provider "aws" {
  region                      = "us-east-1"
  access_key                  = "mock"
  secret_key                  = "mock"
  s3_use_path_style           = true
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true

  endpoints {
    s3         = "http://127.0.0.1:4566"
    dynamodb   = "http://127.0.0.1:4566"
    cognitoidp = "http://127.0.0.1:5001"
  }
}

機能概要

ローカルですが サインアップ / ログイン / ログアウト /メール変更 / パスワード変更 /退会

管理画面では、ユーザー一覧表示 / 削除 / 権限設定 を含むフロー一式を再現しています。

  • http://localhost:5173/

ログイン、サインアップ、管理画面ログインへの Path を表示させてみる

home

  • http://localhost:5173/signup

サインアップすると、mailhog でメール受信しリンクを押下するとアカウントが有効化される

有効化

  • http://localhost:5173/login

ログイン

  • http://localhost:5173/user/dashboard

ログイン後ページを用意してみる

ダッシュボード

  • http://localhost:5173/admin/dashboard

管理画面も用意してみる

ダッシュボード


■ 処理フロー

1. ログイン

  • Cognito にメール + PW を送信
  • AccessToken と RefreshToken を発行
  • RefreshToken は httpOnly Cookie に保存
  • AccessToken はメモリ or localStorage に保存

2. API 実行

各 API へのリクエストは Authorization ヘッダーにトークンを付与します。 全部で、10-15 個ぐらいエンドポイントがあり、サインアップとかログイン以外は、認証を通さないと 401 を返します。

React (accessToken付与) ──────────────▶ Express (認可OK)

3. AccessToken が期限切れ(401)

- AccessToken期限切れ(401)
React
   └─ axios interceptor が自動で refresh-tokens を実行
        POST /auth/refresh-tokens(Cookie自動送信)
       Express ─────────▶ Cognito(Moto)
                       ◀──────── AccessToken

- アクセストークン更新 → 直前の API を自動再実行

4. RefreshToken ローテーション

AWS のアップデートで出来るようになったみたいなので、やればよかったけどやってないです、、、


最後に

検証にあんまり時間かけたくなかったので、雑な感じになってしまった。 TODO もいくつか残してるので、気が向いたときにやろうかな..

Nifty tech tag lists from Wouter Beeftink