AWS Cognitoをdockerでローカル開発環境で使う
cognitoってローカルで使える?
cognitoを使いたいけど、開発するときにaws上で使うとコストがかかるし、やりづらいって思うあなたへ!ローカルで管理しやすくてコストなしでできます。例のアプリを作りましょう。
事前準備
事前に下記のソフトをインストールする必要があります。
- Node.js (npm付き)
- Docker
- AWS CLI
まずは、ExpressでWEBサーバを作る
プロジェクト作成
プロジェクトを作って、Node初期設定を行う。
mkdir zenn
cd zenn
npm init
npm initを実行するときに設定できますが、詳しくない方は全部エンター押してもOK!
次はexpressパッケージをインストールする。
npm install express
次はプロジェクトのルートディレクトリで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.jsonでscriptsのところを下記のようにする。
...
"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ファイルを作成します。
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のファイルを作成します。
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のスクリプトに戻って、次のように書きます。
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