AWS CDK でLambdaオーソライザー(トークン)を実装して認証を試してみた

AWS CDK でLambdaオーソライザー(トークン)を実装して認証を試してみた

2025.08.13

こんにちは!製造ビジネステクノロジー部の小林です。

最近Lambdaオーソライザーを使う機会があったので、その理解を深めるために検証を行いました。
API Gatewayでは、認証・認可を独自にカスタマイズすることができます。今回は、AWS CDK と Lambdaオーソライザーを使ってシンプルな固定トークン認証を実装してみました。

Lambdaオーソライザーとは?

Lambdaオーソライザーは、API Gatewayがバックエンドにリクエストを送る前に、認証・認可のロジックを実行するための機能です。
スクリーンショット 2025-08-13 0.27.23
引用:https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html

Lambda関数が「許可 (Allow)」のポリシーを返せばAPIへのアクセスが許可され、「拒否 (Deny)」を返せばアクセスは拒否されます。

Lambdaオーソライザーのタイプ

Lambdaオーソライザーには、認証情報の取得方法によって2つのタイプがあります。

1. トークンベースのオーソライザー

JSON Web Token (JWT) や OAuthトークンなど、単一のベアラートークンで発信者IDを受け取る場合に用います。認証トークンは通常、Authorization ヘッダーから取得します。今回の実装はこちらのタイプです。

ベアラートークンとは?

ベアラートークンとは、クライアントが認証のために提示する文字列形式の認証情報です。
JWT (JSON Web Token) や OAuthトークンなどが代表的な例です。

発信者ID(Principal ID)とは?

発信者IDとは、リクエストを送信したユーザーやアプリケーションを特定するための識別子です。

  • 例えば、JWTのペイロードには、ユーザーIDを示すsub(Subject)クレームが含まれています。
  • Lambdaオーソライザーは、このsubの値を抽出し、principalIdとしてAPI Gatewayに返します。

2. リクエストパラメータベースのオーソライザー

複数のヘッダー、クエリ文字列、ステージ変数など、複数のIDソースに基づいて発信者IDを受け取る場合に使用します。

シンプルな固定トークン認証で実装してみる

ここでは、"allow"という固定の文字列が認証トークンとして送られてきた場合のみ、アクセスを許可するオーソライザーを実装します。

概要図

lambda-authorizer/
├── bin/
│   └── lambda-authorizer.ts
├── lib/
│   └── lambda-authorizer-stack.ts
├── lambda/
│   ├── authorizer.ts
│   └── backend.ts
...

1. Lambdaオーソライザー関数の作成

まず、認証ロジックを記述するLambda関数を作成します。

  • event.authorizationToken: クライアントから送られてきた認証トークンを取得します。
  • 認証ロジック: authorizationTokenが"allow"であるかを確認します。
  • レスポンス: 認証結果に基づいて、IAMポリシーを返します。
lambda/authorizer.ts
import { APIGatewayTokenAuthorizerEvent, APIGatewayAuthorizerResult } from 'aws-lambda';

export const handler = async (event: APIGatewayTokenAuthorizerEvent): Promise<APIGatewayAuthorizerResult> => {
  // ① 認証トークンを取得
  const token = event.authorizationToken;

  // ② 認証ロジック:トークンが'allow'なら許可、それ以外は拒否
  const effect = token === 'allow' ? 'Allow' : 'Deny';

  // ③ IAMポリシーを返す
  return {
    principalId: 'user', // 固定のユーザーID
    policyDocument: {
      Version: '2012-10-17',
      Statement: [{
        Action: 'execute-api:Invoke',
        Effect: effect,
        Resource: event.methodArn,
      }],
    },
  };
};

このソースでは、event.authorizationTokenが"allow"と一致すればeffectが"Allow"となり、アクセスが許可されます。それ以外の値の場合は"Deny"となり、アクセスが拒否されます。

2. バックエンドLambdaのコード

この関数は、オーソライザーの認証を通過したリクエストのみが実行されます。

lambda/backend.ts
import { APIGatewayProxyResult } from 'aws-lambda';

export const handler = async (): Promise<APIGatewayProxyResult> => {
  return {
    statusCode: 200,
    body: JSON.stringify({ message: 'Hello from lambda API' }),
  };
};

3. AWS CDKによるインフラの定義

AWS CDKを使って、API Gateway、Lambda関数などをまとめて定義します。

lambda-authorizer-stack.ts
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';

export class LambdaAuthorizerStack 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, 'AuthorizerFunction', {
      runtime: Runtime.NODEJS_22_X,
      entry: path.join(__dirname, '..', 'lambda', 'authorizer.ts'),
      handler: 'handler',
    });

    // API Gatewayの定義
    const api = new apigw.RestApi(this, 'MyProtectedApi', {
      restApiName: 'MyProtectedApi',
      description: 'API protected by a Lambda authorizer',
    });

    // Lambdaオーソライザーの定義
    const authorizer = new apigw.TokenAuthorizer(this, 'MyLambdaAuthorizer', {
      handler: authorizerFunction,
      identitySource: 'method.request.header.Authorization',
    });

    // バックエンド統合の定義
    const backendIntegration = new apigw.LambdaIntegration(backendFunction);

    // リソースとメソッドの定義
    const helloResource = api.root.addResource('hello');
    helloResource.addMethod('GET', backendIntegration, {
      authorizer: authorizer,
    });
  }
}

上記でリソースの作成は完了です。cdk deployコマンドでデプロイします。

動作確認

デプロイが完了したら、AWSコンソールで作成されたリソースを確認してみます。
スクリーンショット 2025-08-13 0.11.47
スクリーンショット 2025-08-13 0.12.40
スクリーンショット 2025-08-13 0.13.37

Lambdaオーソライザーが作成されていますね!
では、実際にAPIを呼び出して、認証が正しく動作するか確認してみます。curlコマンドを使って動作を確認します。{API_ID}はデプロイされたAPIのエンドポイントIDに置き換えてください。

1. 認証なしでアクセス(失敗パターン)

Authorizationヘッダーがない場合、認証情報が不足しているため401 Unauthorizedが返されます。

curl -i -X GET https://{API_ID}.execute-api.ap-northeast-1.amazonaws.com/prod/hello

結果

HTTP/2 401 
~省略
{"message":"Unauthorized"}% 

2. 無効なトークンでアクセス(失敗パターン)

Authorizationヘッダーに"invalid-token"のような無効なトークンを渡すと、オーソライザーがDenyポリシーを返すため403が返されます。

curl -i -X GET https://{API_ID}.execute-api.ap-northeast-1.amazonaws.com/prod/hello \
  -H "Authorization: invalid-token"

結果

HTTP/2 403
〜省略
{"Message":"User is not authorized to access this resource with an explicit deny"}

3. 有効なトークンでアクセス(成功パターン)

Authorizationヘッダーに"allow"を含めてリクエストを送信すると、オーソライザーがAllowポリシーを返すため、バックエンドのLambda関数が実行されます。

curl -i -X GET https://{API_ID}.execute-api.ap-northeast-1.amazonaws.com/prod/hello \
  -H "Authorization: allow"

結果

HTTP/2 200 
〜省略
{"message":"Hello from lambda API!"}

余談

AWSコンソールからでも、Lambdaオーソライザーの動作確認ができるようです。便利ですね!
スクリーンショット 2025-08-13 0.53.47
スクリーンショット 2025-08-13 0.55.05

おわりに

今回は、Lambdaオーソライザーを使ってAPI Gatewayにカスタム認証を実装する方法を解説しました。シンプルな固定トークン認証を通して、認証フローを確認することができましたね。次回はJWTトークンを利用した認証に挑戦し、よりセキュアなAPIを構築してみます!この記事が皆様のお役に立てば幸いです。

参考記事

https://dev.classmethod.jp/articles/api-gateway-lambda-authorizer-alias-version/

この記事をシェアする

facebookのロゴhatenaのロゴtwitterのロゴ

© Classmethod, Inc. All rights reserved.