こうこく
作 ▸
改 ▸

API GatewayのLambdaオーソライザーで任意の403エラーメッセージをレスポンスする

  • オーソライザーのレスポンスの context を使う。
  • API Gatewayの『ゲートウェイのレスポンス』で、403エラー時にオーソライザーの context から取得した内容を返すようにする。
もくじ

API GatewayのLambdaオーソライザーで Deny を返すと、レスポンスはデフォルトで以下の通りになる。

{"message":"User is not authorized to access this resource with an explicit deny"}

これを自由な形で返せるようにする方法。

オーソライザーに context 設定

まず、Lambdaでオーソライザーを作ります。

API Gateway Lambda オーソライザーの使用 - Amazon API Gateway

↓このオーソライザーを使って認証を行うAPIが、あらかじめ存在すると仮定してくます。お試し用なので、リクエストヘッダの Authorization 値が aaa なら Allow としてます。

オーソライザー
exports.handler = async (event) => {
	// ヘッダのAuthorization値が 'aaa' なら Allow, それ以外なら Deny
	const token = event.headers.Authorization || event.headers.authorization;
	if (token == 'aaa') {
		return generatePolicy('user', 'Allow', event.methodArn);
	} else {
		return generatePolicy('user', 'Deny', event.methodArn, 'だめだめ');
	}
};

const generatePolicy = function(principalId, effect, resource, errorMessage) {
	const res = {
		principalId: principalId,
		policyDocument: {
			Version: '2012-10-17',
			Statement: [
				{
					Action: 'execute-api:Invoke',
					Effect: effect,
					Resource: resource
				}
			]
		}
	};
	if (errorMessage) {
		res.context = {
			response: JSON.stringify({
				error: errorMessage,
				hoge: 'piyo'
			})
		};
	}
	return res;
};

都合によりイベントペイロードがリクエストのオーソライザーですが、気にしないでください。トークンでも同様にできると思います。

大事なのはレスポンスの context.response です。この記事では、認証エラー時にAPI Gatewayでこれをそのままレスポンスするように設定します。

本当にそのままレスポンスボディになるので、JSON.stringify() してます。

ゲートウェイのレスポンス変更

次に、API Gatewayの設定を行います。

API Gatewayのコンソールの左メニューから『ゲートウェイのレスポンス』を開いて、『Access Denied [403]』を編集します。

『マッピングテンプレート』に $context.authorizer.response と入力して保存します。

Access Denied [403]
Access Denied [403]

なお、画像ではレスポンスヘッダーが設定されてますが、これは内容とは関係ありません。エラー時もCORS有効化するために入れてあるだけです。

保存したら、忘れずデプロイしておきます。

結果

デプロイ完了したら、上記のオーソライザーが設定されたAPIを叩きます。(反映に数分かかるかも)

リクエストヘッダの Authorizationaaa 以外を指定していれば、以下のレスポンスがステータスコード403で返ってきます。

{"error":"だめだめ","hoge":"piyo"}

ちゃんとオーソライザーの context.response の通りになってるはずです。

備考

念のため書いておくと、response というのは私が勝手につけたキーなので、マッピングテンプレートとLambdaの内容が対応してればどんな名前でも平気です。context.hoge だったら、マッピングテンプレートは $context.authorizer.hoge です。

それと、この方法はゲートウェイのレスポンスをいじってます。なので、もしオーソライザー以外で『Access Denied [403]』が出るルートが存在する場合、その時にレスポンスがカラになってしまうはずです。context.response なんか設定されてないから。

少し触った限りでは無さそうでしたが、これで本当にどこにも弊害が出ないかは要検証。

この記事に何かあればこちらまで (非公開)