AWSで複数サービス間での共通認証基盤の構築方法!Cognito応用編!

AWSで複数サービス間での共通認証基盤の構築方法!Cognito応用編!

こんにちは!

今回は Cognito の応用編ということで、複数サービス間での共通認証基盤を構築していきたいと思います。よろしくお願い致します!

想定する読者

  • Cognito を極めたいエンジニア
  • AWS の認証基盤に興味があるヒト
  • 複数のサービス間での共通認証基盤を考えているヒト

はじめに

AWS の認証サービスである Cognito を用いて、複数サービス間での共通認証基盤を構築していきます。Cognito には認証を行う UserPool と、認可を行うIdentityPool がございます。

今回のケースでは、IDプロバイダに Cognito UserPool を使わず、Auth0 を使っていきます。そのため認証に Auth0 、認可に Cognito IdentityPool を利用します。なお、これに加え Cognito UserPool を利用して認証基盤を作ることは可能ですが、今回はこのパターンをご紹介しようと思いますのでその点ご了承ください。

それでは構築していきましょう。

アーキテクチャについて

まずはアーキテクチャについて説明していきます。今回は2つのサービスがあると想定して構築していきます。

Auth0 では1つのテナント内に対して1つのユーザープールが与えられています。テナント内では複数のアプリケーションを持つことができるため、サービスごとにアプリケーションを作成します。

これによりサービス A にサインアップしたことがあるユーザーが、サービス B にサインアップしても、ユーザーの ID はひとつとなります。ユーザーがサービス A を利用しているのか、サービス B を利用しているのかの判断は、Auth0 の app_metadata を利用して行われるように、Auth0 の Rules を設定します。

作成した Auth0 の各アプリケーションには、それぞれ AWS の IAM ロールを付与することができるように Cognito Identity Pool を紐付けします。

以上を構築していきます。

Auth0 側での準備

Auth0 では共通の認証基盤を構築していきます。

まずはテナントとアプリケーションを作成していきましょう。今回は複数サービス間での利用となりますので、1つのテナントに2つのアプリケーションを作成してください。加えて各アプリケーションごとに API を作成します。

作成が完了したら、クイックスタートからサンプルをダウンロードして検証環境をローカルに準備いたします。

対象のアプリケーションを選択するとこの画面になります

サンプルには Login と Calling an API の2つがあります。今回は Calling an API のサンプルを選択しました。ダウンロードされたフォルダを見ると、 auth_config.json というファイルがありますので、先ほど作成した API Audience の名前を入力して保存します。以下は Vue を選択した場合です。

{
  "domain": "example.jp.auth0.com",
  "audience": "ServiceA",
  "clientId": "example"
}

ここまで準備が完了したら、Auth0 のアプリケーションの設定画面にて Application URIs の設定を行ってください。認証後やログアウト後の URL を設定いたします。

最後に、Auth0 の「Rules」を設定して完了となります。

ここで述べている「Rules」とは、Auth0 の機能です。Rules を設定すると、ログインをトリガーに関数を実行することができます。これを利用して今回は Auth0 で管理されているユーザーが持つ app_metadata に情報を追記したり、user_data を共有したりしていきます。

本記事の共通認証基盤を実現するには、以下の内容で2つの Rules を作成します。(Auth0の Rules に関するドキュメントはこちらです)

function (user, context, callback) {
    user.app_metadata = user.app_metadata || {};
    user.app_metadata.service = user.app_metadata.service || [];
    
    const service = user.app_metadata.service;
    const client = context.clientName;
    
    // ログインしたことがないサービスの場合は、app_metadataにサービス名を追加
    if (!service.includes('ServiceA') && client === 'ServiceA') service.push('ServiceA');
    if (!service.includes('ServiceB') && client === 'ServiceB') service.push('ServiceB');
    service.sort();

    // app_metadataを更新
    auth0.users.updateAppMetadata(user.user_id, user.app_metadata);
  
    return callback(null, user, context);
  }

こちらはログインした ユーザーの app_metadata に対して使用したアプリケーション名を更新するものです。

次に ユーザーデータを取得する Rules を作成します。

function (user, context, callback) {
  return callback(null, user, context);
}

user にはユーザー情報が、context にはログイン情報が含まれております。内容については以下のドキュメントを確認してください。

なお Auth0 を利用した今回の認証基盤においては、1つのテナントでユーザーを管理しているので、更新されたユーザーの情報はどのアプリケーションからでも同じものを取得することができます。

以上で、Auth0 の設定は完了となります。

AWS 側の設定

AWS 側では ID プロバイダの設定と IAM ロール、Cognito Identity Pool の作成を行います。順を追って説明いたします。

ID プロバイダの設定

IAM のコンソール画面で、「 ID プロバイダ」を選択し作成します。

OpenID Connect を選択し、URL にアプリケーションのドメイン名、対象者にクライアント ID を入力してください。入力する値は Auth0 のアプリケーション画面で確認してください。

一旦はここまでで、Cognito の作業が終わり次第、IAM ロールを作成いたします。

Cognito Identity Poolの作成

次に Cognito Identity Pool を作成します。認証プロバイダーの部分にて Open ID を設定する場所があります。ここに先ほど登録した ID プロバイダが表示されておりますので、チェックをいれて作成ウィザードを進めていきます。

作成後、Identity Pool の名前でロールが作成されております。AuthRole(認証成功時に付与されるロール)と UnAuthRole(認証失敗時に付与されるロール)のふたつが作成されておりますので、それぞれに与えたい権限のポリシーをアタッチします。

あとはアプリケーションを Identity Pool からクレデンシャル情報を取得するように実装すれば、Auth0 からログインしたユーザーに対して IAM ロールを付与することができます。

ログインして動作確認

ここまでの手順で準備は完了です。それでは先ほど準備したサンプルアプリケーションを起動し、Auth0 を使ってログインしてみましょう。

ログイン後、Auth0 のコンソールにてユーザーの app_metadata を確認すると、以下のようにユーザーが利用したサービス名が記載されます。

ServiceA のみにログインしているユーザー
ServiceA と ServiceB にログインしているユーザー

以上で、共通認証基盤が完成いたしました。

今回の注意点としては、ひとつの AWS アカウントに対して同じ URL の ID プロバイダを複数登録することはできないので、サービスごとにAWSアカウントを作成することが MUST となります。

また、Auth0 側の Rules や app_metadata の内容についてはあくまでサンプルになりますので、Auth0 のドキュメントを参考にいろいろ試してみてください!

関連記事

まとめ

Cognito ではできない場合には、ほかの認証サービスの利用も検討してみましょう。それぞれの良いところを組み合わせれば、認証基盤の実現可能な範囲は大きく広がるでしょう。

このブログでは、AWS のサーバーレスサービスについての記事をどんどん公開しておりますので、ご興味のある方はほかの記事もご覧いただければと思います。

認証基盤や AWS に関するサーバーレスな設計は、お気軽にお問い合わせください。