AWS CDK でLambdaオーソライザー(リクエスト)を実装して認証を試してみた
こんにちは!製造ビジネステクノロジー部の小林です。
前回の記事では、Lambdaオーソライザーのイベントペイロード「トークン」を利用して、シンプルな認証機能を実装しました。
今回は、Lambdaオーソライザーのイベントペイロード「リクエスト」を利用した認証機能をAWS CDKを用いて検証してみました。
Lambdaオーソライザーとは?
Lambdaオーソライザーは、API Gatewayがバックエンドにリクエストを送る前に、認証・認可のロジックを実行するための機能です。これは、認証情報を検証するためのLambda関数と、その関数が返すIAMポリシーとの組み合わせで動作します。
イベントペイロードのリクエストとは?
Lambdaオーソライザーのイベントペイロード「リクエスト」タイプは、複数のIDソース(ヘッダー、クエリ文字列、ステージ変数など)に基づいて、認証・認可のロジックを実行する機能です。トークンベースのオーソライザーと比べて、複数の認証情報を扱うことができます。
- トークン
- 単一のヘッダー(例:Authorization)
- リクエスト
- クエリ文字列、ステージ変数、コンテキスト変数など
例えばAPIキーによる認証を行っている場合、特定のAPIキーがヘッダーに含まれていることに加えて、クエリ文字列に特定のユーザーIDが含まれている場合のみアクセスを許可する、といった複雑な認証要件にも対応できます。
やってみた
実際にLambdaオーソライザーのイベントペイロード「リクエスト」タイプの機能を試してみます。
前提
本記事では、API Gateway の REST API を利用します。
リソースは、AWS CDK を利用して構築します。
概要図
request-authorizer/
├── bin/
│ └── request-authorizer.ts
├── lib/
│ └── request-authorizer-stack.ts
├── lambda/
│ ├── request-authorizer.ts
│ └── backend.ts
...
1. Lambdaオーソライザー関数の作成
認証ロジックを記述するLambda関数を、リクエストタイプに合わせて作成します。この関数は、リクエストのヘッダーとクエリ文字列から認証情報を取得します。
import { APIGatewayRequestAuthorizerEvent, APIGatewayAuthorizerResult } from 'aws-lambda';
export const handler = async (event: APIGatewayRequestAuthorizerEvent): Promise<APIGatewayAuthorizerResult> => {
// ① 認証に必要な情報をリクエストから取得
const apiKey = event.headers?.['x-api-key'];
const user = event.queryStringParameters?.['user'];
// ② 認証ロジックの実行
let effect: 'Allow' | 'Deny';
let principalId: string;
if (apiKey === 'request-key' && user === 'test-user') {
// 認証情報が正しい場合
effect = 'Allow';
principalId = user;
} else {
// 認証情報が不正な場合
effect = 'Deny';
principalId = 'unknown-user';
}
// ③ IAMポリシーを返す
return {
principalId: principalId,
policyDocument: {
Version: '2012-10-17',
Statement: [{
Action: 'execute-api:Invoke',
Effect: effect,
Resource: event.methodArn,
}],
},
};
};
このソースでは、APIGatewayRequestAuthorizerEventという型を利用することで、headersやqueryStringParametersにアクセスできます。複数の条件を満たす場合にのみAllowポリシーを返すよう、ロジックを実装しています。
2. バックエンドLambdaのソース
この関数は、オーソライザーの認証を通過したリクエストのみが実行されます。
import { APIGatewayProxyResult } from 'aws-lambda';
export const handler = async (): Promise<APIGatewayProxyResult> => {
return {
statusCode: 200,
body: JSON.stringify({ message: 'Hello from a protected API!' }),
};
};
3. AWS CDKによるインフラの定義
AWS CDKを使って、API Gateway、Lambda関数などをまとめて定義します。
import { Stack, StackProps, aws_apigateway as apigw } from 'aws-cdk-lib';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import { Runtime } from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';
import * as path from 'path';
// import * as sqs from 'aws-cdk-lib/aws-sqs';
export class RequestAuthorizerStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
// バックエンドLambda関数の定義 (変更なし)
const backendFunction = new NodejsFunction(this, 'BackendFunction', {
runtime: Runtime.NODEJS_22_X,
entry: path.join(__dirname, '..', 'lambda', 'backend.ts'),
handler: 'handler',
});
// Lambdaオーソライザー関数の定義
const authorizerFunction = new NodejsFunction(this, 'AuthorizerRequestFunction', {
runtime: Runtime.NODEJS_22_X,
entry: path.join(__dirname, '..', 'lambda', 'request-authorizer.ts'),
handler: 'handler',
});
// API Gatewayの定義
const api = new apigw.RestApi(this, 'MyProtectedRequestApi', {
restApiName: 'MyProtectedRequestApi',
description: 'API protected by a Lambda request authorizer',
});
// Lambdaオーソライザーの定義
const authorizer = new apigw.RequestAuthorizer(this, 'MyLambdaRequestAuthorizer', {
handler: authorizerFunction,
identitySources: ['method.request.header.x-api-key', 'method.request.querystring.user'],
});
// バックエンド統合とメソッドの定義
const backendIntegration = new apigw.LambdaIntegration(backendFunction);
const helloResource = api.root.addResource('hello');
helloResource.addMethod('GET', backendIntegration, {
authorizer: authorizer,
});
}
}
認証情報の取得元を指定する
identitySourcesプロパティ(Lambdaオーソライザー関数の定義部分)は、Lambdaオーソライザーが認証情報を取得する場所を指定する設定です。
- 'method.request.header.x-api-key': APIリクエストのヘッダーにあるx-api-keyという名前の値を認証情報として使用します。
- 'method.request.querystring.user': APIリクエストのクエリ文字列にあるuserという名前の値を認証情報として使用します。
この設定をすることで、API Gatewayはこれらのパラメータをリクエストから抽出し、オーソライザーのLambda関数に渡してくれます。Lambda関数側では、これらの情報がevent.headersやevent.queryStringParametersとして利用できます。
以上でリソースの作成は完了です!さっそくデプロイしてみましょう!
動作確認
デプロイが完了したら、AWSコンソールで作成されたリソースを確認してみます。
Lambdaオーソライザーのイベントペイロードが「リクエスト」として作成されていますね。
では、実際にAPIを呼び出して、認証が正しく動作するか確認してみます。curlコマンドを使って動作を確認します。{API_ID}はデプロイされたAPIのエンドポイントIDに置き換えてください。
1. 条件を満たさない場合(失敗パターン)
以下のリクエストは、identitySourcesで必須に設定したクエリ文字列(userパラメータ)がリクエストに含まれていないため、401 Unauthorizedが返されます。
curl -i -X GET "https://{API_ID}.execute-api.ap-northeast-1.amazonaws.com/prod/hello" -H "x-api-key: request-key"
結果
HTTP/2 401
~省略
{"message":"Unauthorized"}%
2. 両方の条件を満たす場合(成功パターン)
正しいAPIキーヘッダーと必須のクエリ文字列を両方含めることで、オーソライザーがAllowポリシーを返し、バックエンドのLambda関数が正常に実行されます。
curl -i -X GET "https://{API_ID}.execute-api.ap-northeast-1.amazonaws.com/prod/hello?user=test-user" -H "x-api-key: request-key"
結果
HTTP/2 200
〜省略
{"message":"Hello from lambda API!"}
コンソールから動作確認
おわりに
今回は、API GatewayのLambdaオーソライザー(リクエスト)を利用して、複数のパラメータを組み合わせた認証機能を検証してみました。イベントペイロードでリクエスト型を選択することで、単一の認証要素では実現できない、細かい制御が可能になりましたね。この記事がLambdaオーソライザーの利用を検討している方のお役に立てば幸いです。