Amazon Cognito グループの説明属性をアプリケーションから取得する

SCSKの畑です。3回目の投稿です。

分量多めのエントリが続いたので、今回は小ネタです。

アーキテクチャ概要

今回はおおよそアプリケーション部分(下図で言えば Nuxt.js)及び Amazon Cognito に閉じた話になりますが、一応載せておきます。解決策のセクションで AppSync や Lambda についても言及はしますが・・

構築・開発中のアプリケーションアーキテクチャ概要図です。

小ネタ本題

現在作成している Web アプリケーションでは、上図の通り Cognito でユーザ認証したり、AppSync API を実行している関係上、モジュールとして aws-amplify を使用しています。ここで改めて説明するまでもなく便利に使えています。

例えば、Cognito で認証したユーザの各種属性については、以下のようなコードで簡単に取得することができます。特に、下記コードにおける「custom:approle」のようなカスタム属性についても fetchUserAttributes() の返り値としてまとめて取得できるのが大変便利です。

import { fetchUserAttributes, getCurrentUser } from "aws-amplify/auth";

const userAttributes = await fetchUserAttributes()
const currentUser = await getCurrentUser()

const user_id = currentUser['username']
const user_fullname = `${userAttributes['family_name']} ${userAttributes['given_name']}`
const user_email = userAttributes['email']
const user_approle = userAttributes['custom:approle']

同様に、ユーザが属している Cognito グループの情報については以下のように取得できます。こちらも簡単なコードですし、リストで取得できるのでユーザが複数グループに属している場合も問題ないのですが・・

import { fetchAuthSession } from "aws-amplify/auth";

const { tokens: cognito_session } = await fetchAuthSession();

const user_belong_group = cognito_session.accessToken.payload['cognito:groups']

実はこのコードで取得できる情報って、Cognito グループの「グループ名(GroupName)」のみで、「説明(Description)」は取得できません。そもそも「説明(Description)」って何?という話があるかもしれませんが、以下画面の通りグループの説明なり備考なりを記載するような属性のことです。

Cognitoグループの編集画面

で、本案件においてはこの属性を Cognito グループの論理名として使用しています。つまり、アプリケーションの画面でグループ名(物理名)の代わりに表示するために aws-amplify 経由で合わせて取得したかったのですが、上記コードでは取得できなかったのでちょっと困りました、というのが今回の話になります。

なお、当初は aws-amplify モジュール依存の問題かと思ってたのですが、ID token の中身を調べてみたところそもそもこの情報が含まれていないことが分かりました。よくよく考えるとそりゃそうだよな、という感じではありますが・・

ID (ID) トークンについて - Amazon Cognito
ID トークンの使用

もちろん、この属性をグループの論理名として使用すること自体が正直アンチパターン寄りではあるとは思います。入力可能な文字数の多さから見ても、本来はグループ自体の説明なり管理のための情報を入力するような属性でしょうし。ただ、そう思いつつも

  • 他にそのような目的で使用できる属性がない
  • Cognito の外側に「グループの論理名」情報を持たせたくない
    • グループの変更・メンテナンス時に二重メンテナンスが必要になってしまう
      • 将来的にお客さんへの引き継ぎを考えた時にシンプルな情報の持たせ方にしておきたい
    • 他にグループに紐つけて持たせたい情報があれば検討の余地があるが、論理名だけだと尚更・・
  • グループ名(物理名)自体に論理名の情報を包含してしまうのも手だが、抵抗感がある
    • ユーザ(お客さん)の組織をグループにマッピングする仕様とする予定のため、今後の組織変更等で組織名(論理名)のみが変更になった場合の対応コストが嵩んでしまう

という理由から、今回は消極的にこのような設計としています。

よって、Cognito グループの「グループ名(GroupName)」「説明(Description)」の両情報を取得するような仕組みを別に用意する必要がありました。

解決策

と言っても特に捻りのある内容ではなく、単純に Cognito グループ情報取得用の AppSync GraphQL query 及び Lambda を作成しただけです。query 定義自体は @function 句で lambda を紐付けただけなので、Lambda の実装例のみ載せてみます。

ランタイムは Python です。また、list_groups() を実行にあたり、Lambda の実行ロールに「cognito-idp:ListGroups」が必要となります。

def get_all_groups():
    try:
        client = boto3.client('cognito-idp')
        
        all_groups = []
        response = client.list_groups(UserPoolId = USER_POOL_ID)
        groups = response.get('Groups', [])
        for group in groups:
            all_groups.append({'name': group['GroupName'], 'description': group['Description']})
        
        return all_groups
    except Exception as e:
        raise

アプリケーションの作り上、特定ユーザが属しているグループの情報を取得するのではなく、グループ名と説明のマッピング一式を取得できた方が潰しが効くためそのような実装としています。

まとめ

Cognito グループに設定できる属性の種類、もう少し増えても良いように思うんですがどうなんでしょうね。結局、実用上は Cognito ユーザ側に属性として持たせてしまえば良いのと(他 IdP との連携を考えると尚更)、Cognito 内におけるグループの主な役割が所属ユーザへの IAM ロール割当てになっていることから、他の情報を持たせる必要はないよね?という思想なのかなと想像しますが・・

本記事の内容がどなたかの役に立てば幸いです。

タイトルとURLをコピーしました