AWS AppSync のリゾルバ内で Amazon Cognito ユーザーの email 属性やカスタム属性を取得する

こんにちは、広野です。

Amazon Cognito 認証を使用してアプリから AWS AppSync API を呼び出すとき、デフォルトではリゾルバのみでは Cognito ユーザーの ID や Cognito グループぐらいしか属性を取得できません。

Cognito ユーザーの ID さえわかれば、AWS Lambda 関数で Amazon Cognito の API を呼び出せば他の属性を取得することはできます。ですが、AWS Lambda 関数を使用することでレスポンスが遅くなるのは嫌です。本記事では、AWS Lambda 関数抜きで実現する方法を紹介します。

背景

まず、AWS 公式ドキュメントを読むと。 ※ここでは、JavaScript リゾルバで説明します。VTL も文法は違えど、内容は同じです。

長々と書いてありますが、コンテキストと呼ばれる引数 (ctx) の中に、AWS AppSync がアプリから受け取った情報が格納されます。Amazon Cognito ユーザー関連の情報は ctx.identity の中にあります。

ログに落とすと以下のようなデータが格納されています。

{
    "identity": {
        "claims": {
            "sub": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
            "device_key": "ap-northeast-1_f074a9ab-30c8-48d2-9ff8-3f265cc90368",
            "cognito:groups": [
                "BASIC"
            ],
            "iss": "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXX",
            "client_id": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
            "origin_jti": "bca4c698-3df8-4d2d-a224-ac0095385a98",
            "event_id": "48eeef11-6aa5-4e77-81f7-69c6e4faaeae",
            "token_use": "access",
            "scope": "aws.cognito.signin.user.admin",
            "auth_time": 1735803851,
            "exp": 1736061086,
            "iat": 1736039486,
            "jti": "317acaa7-fa7b-4ff5-ad32-f0a02b399664",
            "username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        },
        "defaultAuthStrategy": "ALLOW",
        "groups": [
            "BASIC"
        ],
        "issuer": "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXX",
        "sourceIp": [
            "xxx.xxx.xxx.xxx"
        ],
        "sub": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    }
}

一通り見て気付くことは、以下です。

  • ユーザー ID、ユーザー名、グループ属性はある
  • email 属性が無い
  • カスタム属性が無い

例えばユーザーのメールアドレスを使用した処理をしたいとか、カスタム属性によって処理の実行を制御したいときに、このままでは AWS Lambda 関数が必要になってしまいます。AWS AppSync を使うからには、AWS Lambda 関数は使用したくありません。

こうなっている原因は、説明が後回しになりましたが赤線を引いた “token_use”: “access” という部分で表されています。

AWS AppSync はデフォルトでは JWT トークンの中のアクセストークンが使用され、アクセストークン内に含まれている情報が限られているためにこの状況が発生しています。

比較して、Amazon API Gateway で Cognito オーソライザーを使用するときは ID トークンを使用します。以前私が書いた記事を紹介しますが、リクエストの Authorization ヘッダーに ID トークンを格納して送る、と紹介していました。

AWS AppSync でも、ID トークンを使用した Cognito 認証ができれば、アクセストークンよりもユーザー情報が多く格納されるため、この問題を解決できるのでは?と思った次第です。

解決方法

解決方法は、アプリから AWS AppSync API にリクエストを送るときの Authorization ヘッダーに ID トークンを格納することになります。

ヒントとなるドキュメントは、AWS Amplify UI のドキュメントにあります。カスタムヘッダーを設定するコードのサンプルが紹介されています。

ただし、これだけでは具体的にどう書けば今回のユースケースが動くのかわかりませんでしたが、色々と調べた結果動く方法が見つかりました。React の例になりますが、以下のコードになります。

import { Amplify } from 'aws-amplify';
import { Authenticator, useAuthenticator } from '@aws-amplify/ui-react';
import { fetchAuthSession } from 'aws-amplify/auth';

//Cognito, AppSync 連携設定
Amplify.configure(
  {
    Auth: {
      Cognito: {
        userPoolId: import.meta.env.VITE_USERPOOLID,
        userPoolClientId: import.meta.env.VITE_USERPOOLWEBCLIENTID,
        identityPoolId: import.meta.env.VITE_IDPOOLID
      }
    },
    // 通常は、以下の部分だけを書く。
    API: {
      GraphQL: {
        endpoint: import.meta.env.VITE_APPSYNC_URL,
        region: import.meta.env.VITE_REGION,
        defaultAuthMode: 'userPool',
      }
    }
  },
  // 追加で以下のカスタムヘッダーを上とは切り離して書く。(これが重要)
  {
    API: {
      GraphQL: {
        headers: async () => ({
          Authorization: (
            await fetchAuthSession()
          ).tokens?.idToken?.toString()
        })
      }
    }
  }
);

アプリ内に、使用する Amazon Cognito リソースや AWS AppSync リソースを設定するコードを書くのですが、そこにカスタムヘッダーを追加する設定を書きます。ただし、カスタムヘッダーに格納する値は ID トークンですので、動的に変わります。Amazon Cognito の設定が反映されている前提で、Amazon Cognito にユーザーのセッション情報を問い合わせる fetchAuthSession を実行した結果から ID トークンを抜き出し、格納するコードになっています。

正直内部的な動きはわかっていませんが、こう書くことで AWS AppSync API にリクエストするときのトークンを ID トークンに置き換えることができました。検証した結果、トークンの自動更新も機能していました。

結果

結果、AWS AppSync リゾルバ内で取得するコンテキスト情報がどう変わったかを紹介します。背景の章で紹介したオリジナルのコンテキストと比較するため ctx.identity の中身をお見せします。

{
    "identity": {
        "claims": {
            "sub": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
            "cognito:groups": [
                "BASIC"
            ],
            "email_verified": true,
            "iss": "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXX",
            "cognito:username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
            "origin_jti": "5c010020-e36b-46c5-be5c-579c63b68373",
            "aud": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
            "event_id": "24fb81ef-5e04-4d29-ac28-810ce689f7ac",
            "token_use": "id",
            "auth_time": 1736259678,
            "exp": 1736281278,
            "iat": 1736259678,
            "custom:zokusei": "test",
            "jti": "7e980548-297d-4514-bdd0-7862550f6def",
            "email": "hirono@xxxxx.xxx"
        },
        "defaultAuthStrategy": "ALLOW",
        "groups": [
            "BASIC"
        ],
        "issuer": "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXX",
        "sourceIp": [
            "xxx.xxx.xxx.xxx"
        ],
        "sub": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    }
}

赤線を引いた箇所が変更点です。

“token_use”: “id” になっており、ID トークンが使われていることがわかります。

“custom:zokusei”: “test” が追加されました。これは私がテスト的に Amazon Cognito に追加したカスタム属性です。これでカスタム属性に応じた制御をかけることができますね。

“email”: “hirono@xxxxx.xxx” が追加されました。これで Cognito ユーザーのメールアドレスを使用した処理ができますね。

カスタム属性については、前提として Amazon Cognito のアプリケーションクライアントの「属性権限」設定で、読み取りを有効にしていないと ID トークン経由で連携されません。ご注意ください。

 

さらに補足です。

AWS AppSync が受け取るコンテキストには、リクエストヘッダー情報も含まれています。その中の Authorization ヘッダーの中にトークンが格納されているわけですので、そこから base64 でデコードして同じ情報を取得することも可能と思われます。ですが、結局 ctx.identity.claims に含まれている情報と同じなので、意味はないです。

まとめ

いかがでしたでしょうか。

AWS Lambda 関数を使いたくない一心で調査を頑張りました。本件の解決については、同僚のメンバーにかなり協力して頂きました。感謝!です。

本記事が皆様のお役に立てれば幸いです。

著者について
広野 祐司

AWS サーバーレスアーキテクチャを駆使して社内クラウド人材育成アプリとコンテンツづくりに勤しんでいます。React で SPA を書き始めたら快適すぎて、他の言語には戻れなくなりました。サーバーレス & React 仲間を増やしたいです。AWSは好きですが、それよりもフロントエンド開発の方が好きでして、バックエンド構築を簡単にしてくれたAWSには感謝の気持ちの方が強いです。
取得資格:AWS 認定は13資格、ITサービスマネージャ、ITIL v3 Expert 等
2020 - 2024 Japan AWS Top Engineer 受賞
2022 - 2024 AWS Ambassador 受賞
2023 当社初代フルスタックエンジニア認定
好きなAWSサービス:AWS Amplify / AWS AppSync / Amazon Cognito / AWS Step Functions / AWS CloudFormation

広野 祐司をフォローする

クラウドに強いによるエンジニアブログです。

SCSKクラウドサービス(AWS)は、企業価値の向上につながるAWS 導入を全面支援するオールインワンサービスです。AWS最上位パートナーとして、多種多様な業界のシステム構築実績を持つSCSKが、お客様のDX推進を強力にサポートします。

AWSアプリケーション開発クラウドソリューション
シェアする
タイトルとURLをコピーしました