LoginSignup
0
0

More than 1 year has passed since last update.

AWS CDK - API Gateway(REST)+ Lambda Authorizer

Posted at

前回記事

前提条件

  • IaC は CDK(CDK v2)
  • Lambda のランタイムは Node.js 18(拡張子は.mjs)
  • Lambda の実装は TypeScript(AWS SDK for JavaScript v3)
  • API Gateway の API タイプは REST API

ファイル 構成

前回から Lambda Authorizer を追加している。

  ├─ src/
  │   ├─ lib/
  │   │   ├─ api-gateway/
  │   │   │      ├─ handlers/
  │   │   │      │     └─ sample-api/
  │   │   │      │            └─ index.ts  // Lambdaコード
  │   │   │      │
  │   │   │      ├─ authorizer.ts  // Lambda Authorizer
  │   │   │      └─ resource.ts  // API Gateway + API GatewayがトリガのLambda定義
  │   │   │
  │   │   ├─ sample-stack.ts
  │   │   └─ tsconfig.json
  │   │
  │   └─ main.ts
  │
  // 省略
  │
  ├─ package.json
  ├─ package-lock.json
  └─ tsconfig.json

実装

src/main.ts、src/lib/sample-stack.ts の実装は、前回参照。

src/lib/api-gateway/resource.ts

Authorizer にはCognitoUserPoolsAuthorizerRequestAuthorizerTokenAuthorizer等があるが、Lambda Authorizer の場合は、RequestAuthorizer を使用する。
前回と同様に Lambda Authorizer もtsmjsにコンパイルするためbundlingオプションを指定。
API のルート、メソッド設定はDefining APIsを参考に実装。

src/lib/api-gateway/resource.ts
import * as path from 'path';

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

interface Parameters {
  environment: string;
}

export class ApiGateway {
  private _parameters: Parameters;

  constructor(parameters: Parameters) {
    this._parameters = parameters;
  }

  public createResources(scope: Construct) {
    const runtime = cdk.aws_lambda.Runtime.NODEJS_18_X;

    const bundling = {
      minify: true,
      sourceMap: true,
      externalModules: ['@aws-sdk/*'],
      tsconfig: path.join(__dirname, '../tsconfig.json'),
      format: cdk.aws_lambda_nodejs.OutputFormat.ESM,
    };

    // API Gateway(REST)
    const restApi = new cdk.aws_apigateway.RestApi(scope, 'rest-api', {
      restApiName: `${this._parameters.environment}_rest-api`,
    });

    // Lambda Authorizer
    const authorizerFunction = new cdk.aws_lambda_nodejs.NodejsFunction(scope, 'authorizer-function', {
      functionName: `${this._parameters.environment}_AuthorizerFunction`,
      entry: path.join(__dirname, './authorizer.ts'),
      handler: 'handler',
      runtime,
      memorySize: 1024,
      bundling,
    });

    authorizerFunction.addPermission('authorizer-function-permission', {
      principal: new cdk.aws_iam.ServicePrincipal('apigateway.amazonaws.com'),
      action: 'lambda:InvokeFunction',
    });

    const lambdaAuthorizer = new cdk.aws_apigateway.RequestAuthorizer(scope, 'Authorizer', {
      handler: authorizerFunction,
    });

    // Lambda Role
    const lambdaBasicRole = new cdk.aws_iam.Role(scope, 'lambda-basic-role', {
      assumedBy: new cdk.aws_iam.ServicePrincipal('lambda.amazonaws.com'),
      managedPolicies: [cdk.aws_iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole')],
    });

    // Lambda
    const sampleFunction = new cdk.aws_lambda_nodejs.NodejsFunction(scope, 'sample-function', {
      functionName: `${this._parameters.environment}_sampleFunction`,
      entry: path.join(__dirname, './handlers/sample-api/index.ts'),
      handler: 'handler',
      runtime,
      memorySize: 1024,
      role: lambdaBasicRole,
      bundling,
    });

    // API Gatewayからの実行権限を追加
    sampleFunction.addPermission('sample-function-permission', {
      principal: new cdk.aws_iam.ServicePrincipal('apigateway.amazonaws.com'),
      action: 'lambda:InvokeFunction',
    });

    // ルート設定(/api/groups/{groupId}/items/{itemId}) + Lambda Authorizer 指定
    restApi.root
      .addResource('api')
      .addResource('groups')
      .addResource('{groupId}')
      .addResource('items')
      .addResource('{itemId}')
      .addMethod('GET', new cdk.aws_apigateway.LambdaIntegration(sampleFunction), {
        authorizer: lambdaAuthorizer,
      });
  }
  }
}

src/lib/api-gateway/authorizer.ts

Amazon API Gateway Lambda オーソライザーからの出力に従った形式で値を返却する。
Amazon API Gateway Lambda オーソライザーへの入力に Body は含まれないため、その他要素の検証を実装する。
API タイプが HTTP の場合は、レスポンス形式が異なる。
HTTP API の AWS Lambda オーソライザーの使用

src/lib/api-gateway/authorizer.ts
import * as lambda from 'aws-lambda';

export const handler: lambda.APIGatewayRequestAuthorizerHandler = async (
  event: lambda.APIGatewayRequestAuthorizerEvent,
): Promise<lambda.APIGatewayAuthorizerResult> => {
  try {
    // 何かしらの検証処理を実装
    return allowPolicy(event);
  } catch (error) {
    console.log(error);
    return denyAllPolicy();
  }
};

const allowPolicy = (event: lambda.APIGatewayRequestAuthorizerEvent): lambda.APIGatewayAuthorizerResult => {
  return {
    principalId: 'sample-user-id',
    policyDocument: {
      Version: '2012-10-17',
      Statement: [
        {
          Action: 'execute-api:Invoke',
          Effect: 'Allow',
          Resource: event.methodArn,
        },
      ],
    },
  };
};

const denyAllPolicy = (): lambda.APIGatewayAuthorizerResult => {
  return {
    principalId: '*',
    policyDocument: {
      Version: '2012-10-17',
      Statement: [
        {
          Action: '*',
          Effect: 'Deny',
          Resource: '*',
        },
      ],
    },
  };
};

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0