やってみたら面倒くさい、React アプリからの Amazon Cognito 認証付き AWS Lambda 関数 URL 呼出

こんにちは、広野です。

AWS Lambda 関数 URL がサポートしているレスポンスストリーミングを使用したくて、React アプリから Amazon Cognito ID プール (フェデレーテッドアイデンティティ) の一時的なクレデンシャルを取得し IAM 認証付きの AWS Lambda 関数 URL を呼び出せるようにしようとしたところ、激ハマリしました。

やりたいことは↓コレなんですが、一時的なクレデンシャルを取得して以降の Lambda 関数 URL 呼出に関する AWS 公式情報が具体的なものがなく、ネット上の有志の方のブログ情報を見ても環境の違いやモジュールのバージョン違いのためそのままでは動かず、苦労しました。

以降、動いたコードなどを紹介します。

あらためて、やりたいこと

  1. React アプリに Amazon Cognito でユーザ認証する。
  2. 認証済みユーザに、AWS Lambda 関数 URL を呼び出せる権限を付与する。(AWS IAM ロールによる)
  3. React アプリから、AWS Lambda 関数 URL を呼び出す。

これにより、アプリの認証済みユーザだけが AWS Lambda 関数 URL を呼び出せることになるので、セキュアな仕組みを構築できます。AWS Lambda 関数 URL は CORS もサポートしていますし、アクセスが少なく重要度の低いビジネスロジックであれば API Gateway は不要になります。
(余談) 関数 URL は Amazon API Gateway と違って IPv6 もサポートしています。Amazon CloudWatch Logs に残した AWS Lambda 関数のログを見ると、アクセス元デバイスの IPv6 アドレスが残っていました。AWS WAF はアタッチできないのと、スロットリングとかはできないのでそういう要件が必要になると Amazon API Gateway と統合しないとです。

参考までに、同様の構成で「Amazon Cognito 認証済みユーザのみが Amazon S3 バケットに書き込める」構成を以下ブログに書いておりましたが、このときは AWS が Amazon S3 に書き込むための SDK を用意してくれていたので簡単でした。

Amazon Cognito ID プールから払い出される IAM ロールに AWS Lambda 関数 URL を呼び出す権限をどのように付けるかは、以下に書いてあるように、”Action”: “lambda:InvokeFunctionUrl” を許可してあげれば OK なので簡単です。

続いて AWS Lambda 関数 URL を呼び出す情報は以下に書いてあるんですが、「いや、聞きたいのはそんなことじゃなくてね・・・もちょっと具体的なコードを教えてくれない?」って思う内容でして。

ドキュメントを読んで何がわからないかと言うと。

関数 URL が AWS IAM 認証を使用する場合、リクエストには AWS Signature Version 4 (SigV4) による署名が必要。
って何?
ここについて、実用的な説明がないんですね。細かい、内部仕様?の説明はあるんですが、私の実力ではそれだけじゃコードに落とせなくて。Amazon S3 に書き込むときには、この部分を Wrap した SDK を AWS が用意してくれていたので署名について何も意識せずに済んでいたのです。

で、ネット上をめっちゃ調べて、何とか動くようになった React コードを紹介します。(前置き長っ)

動いた React コード

執筆時点の React モジュール環境は以下です。今後、バージョンアップ等で動かなくなる可能性大です。

  • react 18.2.0
  • aws-amplify 6.0.12
  • @smithy/protocol-http 3.0.12
  • @smithy/signature-v4 2.0.19
  • @aws-crypto/sha256-universal 5.2.0

ちなみに本記事ではアプリ稼働環境に AWS Amplify Console は使っていませんが、Amplify 関連モジュールはあくまでもモジュールなので使えます。

import { fetchAuthSession } from 'aws-amplify/auth';
import { HttpRequest } from '@smithy/protocol-http';
import { Sha256 } from '@aws-crypto/sha256-universal';
import { SignatureV4 } from '@smithy/signature-v4';

const sample = async () => {
  //Lambda関数URLを指定
  const apiUrl = new URL("https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws");
  //Cognito ID プールから一時的なクレデンシャル(IAMアクセスキー等)を取得
  const { credentials } = await fetchAuthSession();
  //このあたりからが、SigV4 署名をするコード。以下のパラメータは絶対必須。
  const signer = new SignatureV4({
    service: 'lambda',
    region: 'ap-northeast-1',
    credentials: {
      accessKeyId: credentials.accessKeyId,
      secretAccessKey: credentials.secretAccessKey,
      sessionToken: credentials.sessionToken
    },
    sha256: Sha256
  });
  //Lambda関数に POST したいパラメータ
  const body = { test: 'test' };
  //以下も変更不可。POSTの場合bodyを含めないとAWS側で署名をverifyしたときにエラーになる。
  const httpRequest = new HttpRequest({
    headers: {
      'Content-Type': 'application/json',
      'Host': apiUrl.hostname
    },
    hostname: apiUrl.hostname,
    method: 'POST',
    protocol: 'https:',
    path: apiUrl.pathname,
    body: JSON.stringify(body)
  });
  const signedRequest = await signer.sign(httpRequest);
  //fetchでLambda関数URLを呼び出す。改めてbodyに加え、署名済みのヘッダー情報を付加する。
  const res = await window.fetch(
    "https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws",
    {
      method: 'POST',
      body: JSON.stringify(body),
      headers: signedRequest.headers
    }
  );
};

前提として、App.js 等に Amplify.configure で Amazon Cognito ユーザープールと ID プールの情報を定義できていないと、fetchAuthSession が機能しません。

AWS Lambda のストリームレスポンスを使用する場合は fetch でないと動作しないようです。axios だと header に host を入れるな、というエラーが出てしまいます。

紹介したコードには固定値が入ってしまっているので適宜修正ください。

まとめ

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

AWS Lambda 関数 URL を使えば Amazon API Gateway はいらなくなるぞー、と息巻いていたのですが、Amazon API Gateway の Cognito オーソライザーと同等の認証をしようとすると やたらめんどくさい w。AWS がそれ用の SDK を出してくれることを待っております・・・。

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

著者について
広野 祐司

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

広野 祐司をフォローする
クラウドに強いによるエンジニアブログです。
SCSKは専門性と豊富な実績を活かしたクラウドサービス USiZE(ユーサイズ)を提供しています。
USiZEサービスサイトでは、お客様のDX推進をワンストップで支援するサービスの詳細や導入事例を紹介しています。
AWSアプリケーション開発クラウドクラウドセキュリティソリューション
シェアする
タイトルとURLをコピーしました