🔐

AWS Cognitoをdockerでローカル開発環境で使う

に公開

cognitoってローカルで使える?

cognitoを使いたいけど、開発するときにaws上で使うとコストがかかるし、やりづらいって思うあなたへ!ローカルで管理しやすくてコストなしでできます。例のアプリを作りましょう。

事前準備

事前に下記のソフトをインストールする必要があります。

  1. Node.js (npm付き)
  2. Docker
  3. AWS CLI

まずは、ExpressでWEBサーバを作る

プロジェクト作成

プロジェクトを作って、Node初期設定を行う。

mkdir zenn
cd zenn

npm init

npm initを実行するときに設定できますが、詳しくない方は全部エンター押してもOK!

次はexpressパッケージをインストールする。

npm install express

次はプロジェクトのルートディレクトリでindex.jsのファイルを作って次のように書く。

index.js
const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

cognitoの使用方法の記事なのでサーバの作成方法について説明しないのですが、もっと知りたい方はこちらを読んでください。

下記コマンドを使うとサーバをアクセスできるようになる。

node index.js

次のリンクをアクセスするとHello World!が表示されたらサーバが無事作成されました。

開発環境の改善

現状はファイルを編集すると反映させるにはnode index.jsのコマンドを再実行しないといけない。
開発しづらいため、nodemonを追加して、自動的に更新させます。

npm install nodemon --save-dev

package.jsonscriptsのところを下記のようにする。

package.json
...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node --env-file=.env index.js",
    "dev": "nodemon --env-file=.env index.js"
  },
...

次はcognitoの接続情報を載せるために.envファイルを作成します。

.env
AWS_COGNITO_ENDPOINT=http://localhost:9229
AWS_COGNITO_USER_POOL_ID=
AWS_COGNITO_USER_POOL_CLIENT_ID=

これからサーバ開始の際に次のコマンドを実行します。

npm run dev

dockerでcognito-localをインストール

(Expressサーバは実行のままでOKです)
ルートディレクトリでdocker-compose.yamlのファイルを作成します。

docker-compose.yaml
  cognito:
    image: jagregory/cognito-local:latest
    build:
      context: ./aws-mock/cognito
    container_name: cognito
    ports:
      - 9229:9229
    environment:
      PORT: 9229
    volumes:
      - type: bind
        source: ./aws-mock/cognito
        target: /app/.cognito

docker-compose upの実行でコンテナが作成されます。
また、プロジェクトではaws-mockディレクトリも作成されました。

└── cognito
    ├── config.json
    └── db
        └── clients.json

Cognitoの初期設定

Cognitoを使うにはまず初期設定が必要です。

AWS CLI初期設定

aws configureのコマンドを実行して初期設定を行う。ローカル環境だけで使いたかったらデフォルトでOKです。

Cognito周りのコマンド

Cognitoを使うにはユーザープールユーザープールクライアントが必要です。
全てのコマンドを一気に実行するスクリプトを最後に準備しましたので、下記に記載されているコマンド理解の上、スクリプトを使ってください。

ユーザープールを作る

aws cognito-idp create-user-pool \
    --pool-name UserPool \
    --query UserPool.Id \
    --output text \
    --endpoint-url "http://localhost:9229"

出力はlocal_1h02Tpubみたいな文字列です。

ユーザープールクライアントを作る

local_1h02Tpubの代わりに前ステップで取得したIDを入力します。

aws cognito-idp create-user-pool-client \
    --client-name UserPoolClient \
    --user-pool-id "local_1h02Tpub" \ 
    --output text \
    --query UserPoolClient.ClientId \
    --endpoint-url "http://localhost:9229"

出力は698068lq8k86i1g9nax7dob7mみたいな文字列です。

ユーザーを作る

local_1h02Tpubの代わりに前ステップ(ユーザプール)で取得したIDを入力します。

aws cognito-idp admin-create-user \      
    --user-pool-id "local_1h02Tpub" \
    --username "t.taro@example.com" \
    --user-attributes Name=email,Value="t.taro@example.com" Name=email_verified,Value=true \
    --message-action SUPPRESS \
    --endpoint-url "http://localhost:9229"

ユーザーのパスワードを設定する

aws cognito-idp admin-set-user-password \
    --user-pool-id "local_1h02Tpub" \
    --username "t.taro@example.com" \
    --password "example123" \
    --permanent \
    --endpoint-url "http://localhost:9229"

簡単なスクリプトで全てを実行する

#!/bin/bash

# コマンドが0以外のステータスで終了した場合、直ちに終了します。
set -e

# --- 設定 ---
# 必要に応じてこれらの値を更新してください

ENDPOINT_URL="http://localhost:9229"
POOL_NAME="UserPool"
CLIENT_NAME="UserPoolClient"
ADMIN_USERNAME="t.taro@example.com"
ADMIN_EMAIL="t.taro@example.com"
ADMIN_PASSWORD="example123"

# --- 設定ここまで ---

echo "Cognito ユーザープール '$POOL_NAME' を作成しようとしています..."

# 1. ユーザープールを作成し、そのIDを取得します
USER_POOL_ID=$(aws cognito-idp create-user-pool \
    --pool-name "$POOL_NAME" \
    --query UserPool.Id \
    --output text \
    --endpoint-url "$ENDPOINT_URL")

# 2. USER_POOL_IDが正常に取得されたか確認します
if [ -z "$USER_POOL_ID" ]; then
    echo "エラー: ユーザープールの作成またはIDの取得に失敗しました。"
    echo "$ENDPOINT_URL で cognito-local が実行されているか確認してください"
    echo "また、AWS CLIが正しく設定されているか確認してください。"
    exit 1
fi

echo "ユーザープールの作成に成功しました。 ID: $USER_POOL_ID"
echo "---"

echo "ユーザープールクライアント '$CLIENT_NAME' を作成しようとしています..."

# 3. ユーザープールクライアントを作成し、そのIDを取得します
USER_POOL_CLIENT_ID=$(aws cognito-idp create-user-pool-client \
    --client-name "$CLIENT_NAME" \
    --user-pool-id "$USER_POOL_ID" \
    --output text \
    --query UserPoolClient.ClientId \
    --endpoint-url "$ENDPOINT_URL")

# 4. USER_POOL_CLIENT_IDが正常に取得されたか確認します
if [ -z "$USER_POOL_CLIENT_ID" ]; then
    echo "エラー: ユーザープールクライアントの作成またはIDの取得に失敗しました。"
    exit 1
fi

echo "ユーザープールクライアントの作成に成功しました。 ID: $USER_POOL_CLIENT_ID"
echo "---"

echo "管理者ユーザー '$ADMIN_USERNAME' を作成しようとしています..."

# 5. 管理者ユーザーを作成します
# コンソールをクリーンに保つため、JSON出力を /dev/null にリダイレクトします
aws cognito-idp admin-create-user \
    --user-pool-id "$USER_POOL_ID" \
    --username "$ADMIN_USERNAME" \
    --user-attributes Name=email,Value="$ADMIN_EMAIL" Name=email_verified,Value=true \
    --message-action SUPPRESS \
    --endpoint-url "$ENDPOINT_URL" > /dev/null

echo "管理者ユーザーが作成されました。"
echo "---"

echo "'$ADMIN_USERNAME' のパスワードを設定しようとしています..."

# 6. 管理者ユーザーのパスワードを設定します
aws cognito-idp admin-set-user-password \
    --user-pool-id "$USER_POOL_ID" \
    --username "$ADMIN_USERNAME" \
    --password "$ADMIN_PASSWORD" \
    --permanent \
    --endpoint-url "$ENDPOINT_URL"

echo "パスワードが設定されました。"
echo "---"

# 7. ここまで到達した場合、すべてのコマンドが成功しています。
#    変数を .env.local に追記します
echo ".env.local に認証情報を追記しています..."

# 'cat <<EOF >>' は、複数行のテキストを安全に追記する方法です
cat <<EOF >> .env.local

# AWS Cognito 認証情報 (ローカル開発用)
AWS_COGNITO_USER_POOL_ID=$USER_POOL_ID
AWS_COGNITO_USER_POOL_CLIENT_ID=$USER_POOL_CLIENT_ID
EOF

echo "セットアップ完了! .env が更新されました。"

これでユーザーが登録されました。次はAPIでログインして、トークンを得る。

APIでログインする

AWSの必要なパッケージをインストる

npm install @aws-sdk/client-cognito-identity-provider

API実装

index.jsのスクリプトに戻って、次のように書きます。

index.js
const CognitoIdentityProviderClient =
  require("@aws-sdk/client-cognito-identity-provider").CognitoIdentityProviderClient;
const AdminInitiateAuthCommand =
  require("@aws-sdk/client-cognito-identity-provider").AdminInitiateAuthCommand;
const express = require("express");
const app = express();
const port = 3000;

app.use(express.json());

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.post("/login", async (req, res) => {
  const client = new CognitoIdentityProviderClient({
    endpoint: process.env.AWS_COGNITO_ENDPOINT,
  });
  const command = new AdminInitiateAuthCommand({
    ClientId: process.env.AWS_COGNITO_USER_POOL_CLIENT_ID,
    UserPoolId: process.env.AWS_COGNITO_USER_POOL_ID,
    AuthFlow: "ADMIN_USER_PASSWORD_AUTH",
    AuthParameters: {
      USERNAME: req.body.email,
      PASSWORD: req.body.password,
    },
  });

  const response = await client.send(command);
  return res.json(response);
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

このコードを書いたらPostmanでhttp://localhost:3000/loginまでPOST APIをコールしたら、下記のようなテキストが返ってきます:

{
    "AuthenticationResult": {
        "AccessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IkNvZ25pdG9Mb2NhbCJ9.eyJhdXRoX3RpbWUiOjE3NjMxMDI5OTUsImNs...",
        "RefreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2duaXRvOnVzZXJuYW1lIjoiNmRiYjI5YjMtYzdhMi00NDU4LWExNGMtOTQs...",
        "IdToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IkNvZ25pdG9Mb2NhbCJ9.eyJjb2duaXRvOnVzZXJuYW1lIjoiNmRiYjI5..."
    },
    "$metadata": {
        "httpStatusCode": 200,
        "attempts": 1,
        "totalRetryDelay": 0
    }
}

このコードについて詳しく知りたい方はこちらでご一読ください。

もちろん、上記のコードをそのまま使わないでください。ただのデモなので、セキュリティチェックが必要です。

ユーザー登録も実装したい方はこちらのリンクを読んでいただいてやってみてください。

以上です。

株式会社ソニックムーブ

Discussion