Cognitoのユーザープールを利用したアプリで、IAM Roleベースのアクセス制御をしてみた

2022.07.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは!DA(データアナリティクス)事業本部 サービスソリューション部の大高です。

最近Cognitoのユーザープールを利用したマルチテナントアプリケーション(いわゆるSaaSアプリ)を開発しているのですが、このアプリケーションからAWS SDK for JavaScript v3を利用してAWSの各種サービスを利用したいなと思いました。

この時に、AWSの各種サービスに対するアクセス制御を「Cognitoのグループ」を利用して制御してみたので、書き残しておきたいと思います。

「ユーザープール」と「テナント」をどう扱うか

複数の「テナント」を持つSaaSアプリケーションにおいて、「ユーザープール」をどのように扱うかをまず最初に考える必要があると思います。

これについては、下記のドキュメントにベストプラクティスとしてまとめられています。

こちらに記載されているように、大きく4つのパターンが考えられます。

  • 「テナント」ごとに「ユーザープール」を作成する
  • 「ユーザープール」は1つにして、「テナント」ごとに「ユーザープール」内の「アプリケーションクライアント」を作成する
  • 「ユーザープール」は1つにして、「テナント」ごとに「ユーザープール」内の「グループ」を作成する
  • 「ユーザープール」は1つにして、ユーザーの属性に「テナント」のIDをもたせる

それぞれメリットデメリットがあるので、アプリケーションに合ったパターンを選択すると良いと思いますが、今回は「グループ」を利用して「テナント」の分離を行いたいと思います。

「グループ」と「IAMロール」ベースのアクセス制御

「グループ」に対して「IAMロール」を割り当てると、ユーザーが所属している「グループ」の「IAMロール」に従って、AWSの各種サービスに対するアクセス制御を行うことができます。これは、Role Based Access Control(RBAC)とも呼ばれたりします。

実際にCognitoを利用するNext.jsやReactアプリケーションでは、AmplifyのAuthenticatorコンポーネントを利用してログイン処理を行い、その後に取得できるクレデンシャル情報を利用することで、各種AWSサービスにアクセスすることができます。

具体的にどのように制御するかを見ていきましょう。

Cognitoユーザープールの設定

AWSの管理コンソールでCognitoの「ユーザープール」を作成したら、「グループ」を作成します。

このときに、「IAMロール」を指定できるので、ここにアクセスをさせたいポリシーを設定した「IAMロール」を割り当てます。

その上で、Cognitoユーザーをグループに所属させることで、該当ユーザーのクレデンシャル情報を利用して「IAMロール」に設定されている権限で各種AWSサービスにアクセスすることができます。

アプリケーション側でのクレデンシャル利用

アプリケーション側でのクレデンシャルの利用方法は非常に簡単です。

AmplifyのAuthenticatorコンポーネントを利用してログインをさせた後に、以下のようにクレデンシャルを取得して利用するだけです。

import { ICredentials } from '@aws-amplify/core'
import { PutObjectCommand, PutObjectOutput, PutObjectRequest, S3Client } from '@aws-sdk/client-s3'
import { Auth } from 'aws-amplify'

export async function sampleFunction(input: PutObjectRequest): Promise<PutObjectOutput> {

  const credentials: ICredentials = await Auth.currentCredentials()
  const s3: S3Client = new S3Client({
    credentials: credentials,
    region: 'ap-northeast-1',
  })
  const command: PutObjectCommand = new PutObjectCommand(input)
  const response: PutObjectOutput = await s3.send(command)

  return response
}

ログイン後にはAuth.currentCredentials()からクレデンシャル情報が取得できるのですが、これが「グループ」に設定した「IAMロール」に基づくクレデンシャルとなります。

このクレデンシャルを、この例ではS3Clientcredentialsとして設定することでアクセス制御を行っています。

利用するS3バケットに対して、「IAMロール」でアクセスを許可するポリシー設定がされていればアクセスできますし、許可されていなければ拒否される、という動作になります。

なお、前提としてCognitoのユーザープールとの紐付け設定は別途必要となるので、下記もご参照ください。

考慮事項

「ユーザー」は複数の「グループ」に所属できるのですが、この場合に「どのIAMロールに従うのか」という点が気になると思います。

これは下記ドキュメントの「グループへの IAM ロールの割り当て」と「グループへの優先順位の値の割り当て」に記載されています。

各グループに優先順位の値を割り当てることができます。優先順位が高い (低い) グループが選択され、それに関連付けられた IAM ロールが適用されます。

記載されている通り、グループの「優先順位」の値に従って「1つのIAMロールが選択される」ことになります。和集合にはならないという点には注意が必要ですね。

まとめ

以上、Cognitoのユーザープールを利用したアプリで、IAM Roleベースのアクセス制御してみました。

マルチテナント形式のアプリケーションではアクセス制御が非常に大事になりますが、「グループ」を利用することでアクセス制御をすることもできるので、選択肢の1つとして検討いただけると良いのではないでしょうか。

どなたかのお役に立てば幸いです。それでは!