ユーザーのデバイスにPush通知を送信するために、以下のような構成でアプリを開発している。

Push通知の送信処理は、Lambda関数でPinpointを呼び出しているため、API Gatewayを経由してLambda関数を実行させる必要がある。そこで、API GatewayとLambdaの連携が必要になるので、忘備録として連携手順を記載する。

Lambda の開始方法

前提条件

  • Lambda関数が作成されていること。
  • Cognitoユーザープール並びに、ユーザーが作成されていること
  • APIリクエストを行うために、Cognito User PoolsからidTokenが取得できていること。

API Gatewayの設定

APIを作成

「APIを作成」からAPI作成画面に移動する。

REST APIを選択し、API名や説明などを入力してAPIを作成する。

API作成画面で設定を行う

  • 新しいAPIを選択
  • API名: 任意の名前を入力
  • 説明: 任意
  • APIエンドポイントタイプ: リージョンを選択

APIの作成が完了すると、以下のように作成ずみのAPIにアクセスできるようになる。

メソッドの作成

APIの詳細画面に遷移し、「メソッドを作成」を押下して、メソッドの作成画面に移動する。

以下の設定を行う。

  • メソッドタイプ: 実装するAPIの機能に応じて「POST」「GET」などを選択する
  • 統合タイプ: Lambda関数
    • 該当するリージョンと関数を選択する。

メソッドを作成すると、作成したメソッドにアクセスできるようになる。

マッピングテンプレートを設定

統合リクエスト > マッピングテンプレート > 編集で、以下のJSONを追記する。

{
  "body": $input.json('$'),
  "headers": {
    #foreach($header in $input.params().header.keySet())
    "$header": "$util.escapeJavaScript($input.params().header.get($header))" #if($foreach.hasNext),#end
    #end
  },
  "method": "$context.httpMethod",
  "params": {
    #foreach($param in $input.params().path.keySet())
    "$param": "$util.escapeJavaScript($input.params().path.get($param))" #if($foreach.hasNext),#end
    #end
  },
  "query": {
    #foreach($queryParam in $input.params().querystring.keySet())
    "$queryParam": "$util.escapeJavaScript($input.params().querystring.get($queryParam))" #if($foreach.hasNext),#end
    #end
  }
}

このマッピングテンプレートは、API Gatewayが受け取ったHTTPリクエストを、Lambda関数などの統合ターゲットに渡す前に変換するためのもの。具体的には、HTTPリクエストから受け取ったデータをJSONオブジェクトの形式に整形している。

以下、各セクションの説明。

  1. body: $input.json('$')
    • リクエストボディ全体をJSON文字列としてキャッチする。
  2. headers:
    • HTTPリクエストのヘッダーを取得し、JSONオブジェクトの形式にマッピング。
  3. method: $context.httpMethod
    • リクエストのHTTPメソッド(例: GET、POST)を取得している。
  4. params:
    • パスパラメーター(例: /users/{userId}のようなURLの一部として埋め込まれているもの)を取得してJSONオブジェクトの形式にマッピングしている。
  5. query:
    • クエリパラメータ(URLの?の後に続くもの)を取得し、JSONオブジェクトの形式にマッピングしている。

Cognitoユーザープールオーソライザーを適用する

この作業は、Amazon API Gateway の API にアクセスできるユーザーを制御するために行う。APIを公開するとエンドポイントのURLが発行される訳だが、誰でもアクセスできてしまっては困る。

そこで、Cognitoユーザープールオーソライザーを適用し、認証されたユーザーのみAPIリクエストを行う方式にすることでセキュリティを高めている。

参照: REST API と Amazon Cognito ユーザープールを統合する

サイドメニューからオーソライザーを作成する。

以下の項目を入力して作成する。

  • オーソライザー名: 適用でOK
  • オーソライザーのタイプ: Cognito
  • Cognitoユーザープール: 該当のものを選択
  • トークンのソース: Authorization(リクエストヘッダーにAuthorizationというフィールドを持たせ、そこに認証情報を付与するため)

オーソライザーを作成すると、詳細画面からテストが実行できるようになる。実際にidTokenを使用してAPIにアクセスできるかどうかを検証できる。

以下のコマンドを実行し、ユーザーの認証情報を取得する。

aws cognito-idp admin-initiate-auth --user-pool-id ${ユーザープールID} --client-id ${アプリクライアントID} --auth-flow ADMIN_NO_SRP_AUTH --auth-parameters USERNAME=xxxxx,PASSWORD=xxxxx

コマンド実行後に以下のエラーが出る場合、IAMユーザーがcognito-idp:AdminInitiateAuth操作を実行する権限がないので、設定する必要がある。IAM (Identity and Access Management)コンソールにて、対象ユーザーにポリシー(AmazonCognitoPowerUser)をアタッチすることで取得できるようになる。

An error occurred (AccessDeniedException) when calling the AdminInitiateAuth operation: User: xxx is not authorized to perform: cognito-idp:AdminInitiateAuth on resource: xxx because no identity-based policy allows the cognito-idp:AdminInitiateAuth action

IdTokenが取得できたら、その値を使ってオーソライザーをテストする。
認証に成功した場合、ステータスコード200が返ってくる。

テストに成功した場合は、クライアント(フロントエンド)側からAPIを呼ぶ際に、リクエストヘッダーに適切なidTokenを指定してあげれば、APIの認証を通過してLambda関数を実行する処理が走る。認証に成功しない場合、Lambda関数は実行されない。

APIをデプロイ

設定が完了したら、APIをデプロイする。(私の話だが、この作業忘れがちなので注意する)

Lambda側の設定

Lambda > 関数 > 該当の関数を選択し、トリガーを追加する。

トリガーを追加設定画面が開くので、以下の設定を行う。

  • API Gateway」を選択
  • Intent: 「Use existing API」を選択し、上記で作成したAPIを選択する。

上記の設定で、Lambda関数の実行トリガーがAPIになるため、クライアント(フロントエンド)側からAPIを正しくコールすれば、Lambda関数が実行される。

これでAPI GatewayとLambdaの連携は完了。

カテゴリー: aws