📝

Cognito 入門を AWS SDK for JavaScript v3 でやってみた

2025/01/25に公開

今から始める Amazon Cognito 入門 #1:ユーザープールと ID プールの違い | DevelopersIO
上記ブログの内容を AWS SDK for JavaScript v3 でやってみました。

前提

  • 実行環境: Lambda
  • ランタイム: Node.js 22.x
  • Lambda 実行ロールの権限: AdministratorAccess
  • Lambda ハンドラーのタイプ: CommonJS
  • Lambda 環境変数
    • EmailAddress: 任意のメールアドレスを指定
    • password: 任意のパスワードを指定
  • タイムアウト: 30 秒

コード

index.js
index.js
const {
  CognitoIdentityProviderClient,
  CreateUserPoolCommand,
  CreateUserPoolClientCommand,
  SignUpCommand,
  AdminConfirmSignUpCommand,
  AdminInitiateAuthCommand,
} = require("@aws-sdk/client-cognito-identity-provider");

const {
  CognitoIdentityClient,
  CreateIdentityPoolCommand,
  GetIdCommand,
  GetCredentialsForIdentityCommand,
  SetIdentityPoolRolesCommand,
} = require("@aws-sdk/client-cognito-identity");

const {
  IAMClient,
  CreatePolicyCommand,
  CreateRoleCommand,
  AttachRolePolicyCommand,
} = require("@aws-sdk/client-iam");

const { STSClient, GetCallerIdentityCommand } = require("@aws-sdk/client-sts");

exports.handler = async function (event, context) {
  const cognitoIdpClient = new CognitoIdentityProviderClient();

  // CreateUserPool
  let input = {
    PoolName: "test",
    AutoVerifiedAttributes: ["email"],
    UsernameAttributes: ["email"],
  };
  let command = new CreateUserPoolCommand(input);
  const userPool = await cognitoIdpClient.send(command);

  // CreateUserPoolClient
  input = {
    UserPoolId: userPool.UserPool.Id,
    ClientName: "test",
    ExplicitAuthFlows: [
      "ALLOW_ADMIN_USER_PASSWORD_AUTH",
      "ALLOW_USER_SRP_AUTH",
      "ALLOW_REFRESH_TOKEN_AUTH",
      "ALLOW_USER_AUTH",
    ],
  };
  command = new CreateUserPoolClientCommand(input);
  const userPoolClient = await cognitoIdpClient.send(command);

  // SignUp
  input = {
    ClientId: userPoolClient.UserPoolClient.ClientId,
    Username: process.env.EmailAddress,
    Password: process.env.password,
    UserAttributes: [
      {
        Name: "email",
        Value: process.env.EmailAddress,
      },
    ],
  };
  command = new SignUpCommand(input);
  await cognitoIdpClient.send(command);

  // AdminConfirmSignUp
  input = {
    UserPoolId: userPool.UserPool.Id,
    Username: process.env.EmailAddress,
  };
  command = new AdminConfirmSignUpCommand(input);
  await cognitoIdpClient.send(command);

  const cognitoIdClient = new CognitoIdentityClient();

  //CreateIdentityPool
  input = {
    IdentityPoolName: "test",
    AllowUnauthenticatedIdentities: false,
    AllowClassicFlow: true,
    CognitoIdentityProviders: [
      {
        ProviderName: `cognito-idp.ap-northeast-1.amazonaws.com/${userPool.UserPool.Id}`,
        ClientId: userPoolClient.UserPoolClient.ClientId,
        ServerSideTokenCheck: false,
      },
    ],
  };
  command = new CreateIdentityPoolCommand(input);
  const identityPool = await cognitoIdClient.send(command);

  const iamclient = new IAMClient();

  //CreatePolicy
  input = {
    PolicyName: "userpool-authorized-policy",
    Path: "/service-role/",
    PolicyDocument: JSON.stringify({
      Version: "2012-10-17",
      Statement: [
        {
          Effect: "Allow",
          Action: "*",
          Resource: "*",
        },
      ],
    }),
  };
  command = new CreatePolicyCommand(input);
  const policy = await iamclient.send(command);

  // CreateRole
  input = {
    Path: "/service-role/",
    RoleName: "userpool-authorized-role",
    AssumeRolePolicyDocument: JSON.stringify({
      Version: "2012-10-17",
      Statement: [
        {
          Effect: "Allow",
          Action: "sts:AssumeRoleWithWebIdentity",
          Principal: {
            Federated: "cognito-identity.amazonaws.com",
          },
          Condition: {
            StringEquals: {
              "cognito-identity.amazonaws.com:aud": identityPool.IdentityPoolId,
            },
            "ForAnyValue:StringLike": {
              "cognito-identity.amazonaws.com:amr": "authenticated",
            },
          },
        },
      ],
    }),
  };
  command = new CreateRoleCommand(input);
  const role = await iamclient.send(command);

  // AttachRolePolicy
  input = {
    RoleName: role.Role.RoleName,
    PolicyArn: policy.Policy.Arn,
  };
  command = new AttachRolePolicyCommand(input);
  await iamclient.send(command);

  // SetIdentityPoolRoles
  input = {
    IdentityPoolId: identityPool.IdentityPoolId,
    Roles: {
      authenticated: role.Role.Arn,
    },
  };
  command = new SetIdentityPoolRolesCommand(input);
  await cognitoIdClient.send(command);

  // AdminInitiateAuth
  input = {
    UserPoolId: userPool.UserPool.Id,
    ClientId: userPoolClient.UserPoolClient.ClientId,
    AuthFlow: "ADMIN_USER_PASSWORD_AUTH",
    AuthParameters: {
      USERNAME: process.env.EmailAddress,
      PASSWORD: process.env.password,
    },
  };
  command = new AdminInitiateAuthCommand(input);
  const idpClient = await cognitoIdpClient.send(command);

  // GetId
  input = {
    IdentityPoolId: identityPool.IdentityPoolId,
    Logins: {
      [`cognito-idp.ap-northeast-1.amazonaws.com/${userPool.UserPool.Id}`]:
        idpClient.AuthenticationResult.IdToken,
    },
  };
  command = new GetIdCommand(input);
  const identityId = await cognitoIdClient.send(command);

  // GetCredentialsForIdentity
  input = {
    IdentityId: identityId.IdentityId,
    Logins: {
      [`cognito-idp.ap-northeast-1.amazonaws.com/${userPool.UserPool.Id}`]:
        idpClient.AuthenticationResult.IdToken,
    },
  };

  await new Promise((resolve) => setTimeout(resolve, 10000));

  command = new GetCredentialsForIdentityCommand(input);
  const credentials = await cognitoIdClient.send(command);

  // GetCallerIdentity
  const stsClient = new STSClient({
    region: "ap-northeast-1",
    credentials: {
      accessKeyId: credentials.Credentials.AccessKeyId,
      secretAccessKey: credentials.Credentials.SecretKey,
      sessionToken: credentials.Credentials.SessionToken,
    },
  });
  command = new GetCallerIdentityCommand();
  const response = await stsClient.send(command);

  return response;
};

実行結果

GetCallerIdentityCommand の結果を取得できれば成功です。

  "$metadata": {
    "httpStatusCode": 200,
    "requestId": "f32331be-2986-4f5f-a0c2-7939de563fe6",
    "attempts": 1,
    "totalRetryDelay": 0
  },
  "UserId": "xxx:CognitoIdentityCredentials",
  "Account": "012345678901",
  "Arn": "arn:aws:sts::012345678901:assumed-role/userpool-authorized-role/CognitoIdentityCredentials"
}

コードについて

フローは以下の通りです。

  1. ユーザープール作成 (CreateUserPoolCommand)
  2. アプリケーションクライアント作成 (CreateUserPoolClientCommand)
  3. サインアップ (SignUpCommand)
  4. ユーザーステータスを確認済みに変更 (AdminConfirmSignUpCommand)
  5. ID プール作成 (CreateIdentityPoolCommand)
  6. IAM ポリシー作成 (CreatePolicyCommand)
  7. IAM ロール作成 (CreateRoleCommand)
  8. IAM ロールに IAM ポリシーをアタッチ (AttachRolePolicyCommand)
  9. ID プールの認証されたロールを設定 (SetIdentityPoolRolesCommand)
  10. ユーザープールでの認証 (AdminInitiateAuthCommand)
  11. アイデンティティ ID 取得 (GetIdCommand)
  12. AWS 一時認証情報の取得 (GetCredentialsForIdentityCommand)
  13. 認証情報の確認 (GetCallerIdentityCommand)

スリープ処理について

await new Promise(resolve => setTimeout(resolve, 10000));

上記のスリープ処理を実装しない場合、GetCredentialsForIdentityCommand 実行時に以下のエラーが発生しました。

InvalidIdentityPoolConfigurationException: Invalid identity pool configuration. Check assigned IAM roles for this pool.

おそらく ID プールの作成や、ID プールの認証されたロールを設定が完了する前に認証情報の取得を試みたことが原因だと思われます。
今回は 10 秒のスリープ処理で成功しましたが、どの程度の時間が必要であるかは不明なので、必要に応じて GetCredentialsForIdentityCommand 実行時のリトライ処理の実装もご検討ください。

その他

今回は以下の点については考慮していません。

  • エラーハンドリング
    • 必要な場合は try...catch やリトライを実装してください
  • input で指定したパラメータの保存
    • 必要な場合は input 変数を使いまわさず都度別の変数に格納してください
  • サービスごとの API 呼び出しのモジュール化
    • 必要な場合はサービスごとにモジュール化してください
  • 機密情報の扱い
    • 必要な場合は環境変数の暗号化や Secrets Manager などの利用を検討してください
  • Lambda 以外からの実行
    • EC2 やローカル環境から実行する場合には適宜コードの変更を行ってください
      • 例: ハンドラー関数の変更、環境変数の設定など

各 API に関する仕様は AWS SDK for JavaScript v3 のリファレンスをご参照ください。

まとめ

今回は Cognito 入門を AWS SDK for JavaScript v3 でやってみました。
どなたかの参考になれば幸いです。

参考資料

Discussion