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
と入力して保存します。
なお、画像ではレスポンスヘッダーが設定されてますが、これは内容とは関係ありません。エラー時もCORS有効化するために入れてあるだけです。
保存したら、忘れずデプロイしておきます。
結果
デプロイ完了したら、上記のオーソライザーが設定されたAPIを叩きます。(反映に数分かかるかも)
リクエストヘッダの Authorization
に aaa
以外を指定していれば、以下のレスポンスがステータスコード403で返ってきます。
{"error":"だめだめ","hoge":"piyo"}
ちゃんとオーソライザーの context.response
の通りになってるはずです。
備考
念のため書いておくと、response
というのは私が勝手につけたキーなので、マッピングテンプレートとLambdaの内容が対応してればどんな名前でも平気です。context.hoge
だったら、マッピングテンプレートは $context.authorizer.hoge
です。
それと、この方法はゲートウェイのレスポンスをいじってます。なので、もしオーソライザー以外で『Access Denied [403]』が出るルートが存在する場合、その時にレスポンスがカラになってしまうはずです。context.response
なんか設定されてないから。
少し触った限りでは無さそうでしたが、これで本当にどこにも弊害が出ないかは要検証。