こんにちは、広野です。
Amazon API Gateway で Cognito オーソライザーを使用するときに、Amazon API Gateway の API タイプにより ID トークンを送信するフォーマットが異なります。知らないとハマることがあるため、小ネタとして仕組みも含め紹介します。ここでは、よく使用する REST API と HTTP API での違いについて触れます。
REST API、HTTP API の機能の違いについてはこちら。HTTP API は REST API の機能を削減、安価にしてリニューアルしたような感じです。
やりたいこと
- Amazon Cognito を使用したサーバーレスアプリケーションで、API Gateway にリクエストをかけるときに Cognito オーソライザーで認証をかけたい。
- API Gateway の API タイプは REST API と HTTP API とする。
- サンプルとして紹介するアプリコードの開発フレームワークは React とする。
実現方法
アーキテクチャ
- ユーザがアプリ経由で Amazon Cognito に認証を受けてログインする。
- ログインすると、アプリは Amazon Cognito からいくつかのトークンを取得する。
- ユーザがアプリ経由で Amazon API Gateway にリクエストをかける。その際、リクエストに ID トークンを付加する。
本記事では、ここで付加する ID トークンのフォーマットについて主に説明する。 - リクエストを受けた Amazon API Gateway は、リクエストに付加された ID トークンを検証するため、Amazon Cognito に問い合わせする。
- ID トークン検証の結果、問題なければ後続の処理を進める。(AWS Lambda の呼び出し等)
Amazon API Gateway での Cognito オーソライザーの設定
REST API、HTTP API のいずれも簡単な設定のみです。
REST API の場合
「オーソライザー」メニューで「オーソライザー」を作成します。タイプは Cognito を選択、トークンのソースは Authorization と入力します。認証用の Amazon Cognito ユーザープールもプルダウンで選択できます。
リソースメニューから該当するメソッドを選択し、メソッドリクエストを開くと「認可」の設定があるので、そこで先ほど作成した「オーソライザー」を関連付けます。
詳細は以下の公式ドキュメントをご覧下さい。
HTTP API の場合
「認可」メニューから、「オーソライザーを管理」タブを選択し、オーソライザーを作成します。
オーソライザーのタイプは JWT を選択します。ID ソースは、$request.header.Authorization と書きます。ID プロバイダーの発行者URI には、Amazon Cognito ユーザープールの URI を以下のような書式で書きます。対象者は Amazon Cognito ユーザープールのアプリケーションクライアント ID になります。
オーソライザーが作成できたら、「オーソライザーをルートにアタッチ」タブからメソッドに関連付けます。
詳細は以下の公式ドキュメントをご覧下さい。
アプリ側の API 呼出・ID トークン送信
ここでは、React アプリで axios を使用した API 呼出、ID トークン送信のコードを紹介します。
まず、アプリから Amazon Cognito へのユーザ認証を済ませると、アプリは Amazon Cognito から「自分のユーザ情報のかたまり」のようなものを受け取ります。この中に、ID トークンが含まれています。
アプリがその「自分のユーザ情報のかたまり」を user という変数で受け取ったとすると、ID トークンは user.signInUserSession.idToken という記述で取得できます。ID トークンはエンコードされた英数字の文字列なので、console.log 等でそのまま表示させても何が何だかわかりませんが、デコードするとユーザ情報やシークレットが格納されていることがわかります。Amazon API Gateway の Cognito オーソライザーを使用するときには、この ID トークンを使用するので API 呼出をするときにデータと一緒に送信する必要があります。
トークンについてはこちら。
以下は、React アプリから ID トークン付きで API を呼び出すコードのサンプルです。Amazon Cognito から取得した ID トークンは props.idToken というパラメータで親コンポーネントから引き渡され、idtoken 変数に格納するように書いています。
axios で ID トークンを Amazon API Gateway に送信するときは headers という項目名で送ることになるのですが、送信先が REST API か HTTP API かで、そのフォーマットに違いがあります。
import React, { useState, useEffect } from 'react'; import axios from 'axios'; const Sample = (props) => { const idtoken = props.idToken const headerdata = //ここで REST API と HTTP API でフォーマットに違いがある! const [data, setData] = useState([]); const getData = async (p1, p2, p3) => { const res = await axios.post( process.env.REACT_APP_GETDATA_URL + "/xxxxxxx", //送信先API Gateway URL { param1: p1, param2: p2, param3: p3 }, { headers: headerdata } ); setData(res.data.Items); }; useEffect(() => { getData("AAA","BBB","CCC"); }, []); return ( <React.Fragment> {/* 中略 */} </React.Fragment> ); }; export default Sample;
ここでは、headerdata という変数に各APIに適したフォーマットで ID トークンを格納し送信するようにします。いずれも JSON フォーマットになるのは共通です。
REST API の場合
const headerdata = { "Authorization": idtoken };
REST API の場合は、Authorization という項目名で単純に ID トークンを格納するだけです。
HTTP API の場合
const headerdata = { "Authorization": "Bearer " + idtoken };
HTTP API の場合は、Authorization という項目名は同じなのですが、ID トークンの先頭に “Bearer ” という接頭辞を付ける必要があります。Bearerの後には半角スペースがあります。
これは知らないと、ハマります。
まとめ
いかがでしたでしょうか?
同じ API Gateway でも、REST API と HTTP API で Amazon Cognito へのオーソライザー機能実装手順は大きく異なります。やっていることは同じなんですが。
本記事がみなさまのお役に立てれば幸いです。