APIキーが必要なAPI Gatewayで、APIキーが無い場合に401を返す (AWS SAM with OpenAPI)

APIキーが無い場合に401を返すAPI Gatewayを作成してみました。
2024.02.09

APIキーが必要なAPI Gatewayでは、APIキーが無い場合に403が返ります。

  • APIキーが無い: 403
  • APIキーはあるけど、違う:403

これらについて、403ではなく、401を返すようにしてみました。

おすすめの方

  • AWS SAMでAPI GatewayとLambdaを作成したい方
  • AWS SAMでOpenAPIでAPI Gatewayを作成したい方
  • AWS SAMでAPIキーが必要なAPI Gatewayを作成したい方
  • OpenAPIで作るAPI GatewayでAPIキーが無い場合に401を返したい方

APIを作成する

sam init

sam init \
    --runtime python3.11 \
    --name api-gateway-need-api-key-sample \
    --app-template hello-world \
    --no-tracing \
    --no-application-insights \
    --structured-logging \
    --package-type Zip

AWS SAMテンプレート

template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: ApiGateway-Need-API-Key-Sample

Resources:
  MyApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: dev
      OpenApiVersion: 3.0.1
      ApiKeySourceType: HEADER
      Auth:
        ApiKeyRequired: true
        UsagePlan:
          CreateUsagePlan: PER_API
          UsagePlanName: Basic-usage-plan-of-AWS-SAM-api-gateway-plan
          Quota:
            Limit: 500
            Period: MONTH
          Throttle:
            BurstLimit: 100
            RateLimit: 50
      DefinitionBody:
        Fn::Transform:
          Name: AWS::Include
          Parameters:
            Location: s3://cm-fujii.genki-deploy/ApiGateway-Need-API-Key-Sample/api.yaml

  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.9
      Timeout: 5
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get
            RestApiId: !Ref MyApi

  HelloWorldFunctionLogGroup:
      Type: AWS::Logs::LogGroup
      Properties:
        LogGroupName: !Sub /aws/lambda/${HelloWorldFunction}

OpenAPI

「x-amazon-apigateway-gateway-responses」を利用して、401を返します。

api.yaml

openapi: 3.0.1
info:
  title: ApiGateway-Need-API-Key-Sample
  version: 1.0.0

# https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/supported-gateway-response-types.html
x-amazon-apigateway-gateway-responses:
  INVALID_API_KEY:
    statusCode: 401

paths:
  /hello:
    get:
      tags:
        - hello
      responses:
        200:
          $ref: "#/components/responses/200"
        401:
          $ref: "#/components/responses/401"
        403:
          $ref: "#/components/responses/403"
        500:
          $ref: "#/components/responses/500"
      security:
        - apiKey: []
      x-amazon-apigateway-integration:
        type: aws_proxy
        uri:
          'Fn::Sub': >-
            arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HelloWorldFunction.Arn}/invocations
        httpMethod: POST
        responses:
          default:
            statusCode: 200
        passthroughBehavior: when_no_templates
        contentHandling: CONVERT_TO_TEXT

components:
  securitySchemes:
    apiKey:
      type: apiKey
      name: x-api-key
      in: header

  responses:
    200:
      description: Success
    401:
      description: Unauthorized
    403:
      description: User or client is not authorized.
    500:
      description: Internal Server Error

Lambdaコード

app.py

import json


def lambda_handler(event, context):

    return {
        "statusCode": 200,
        "body": json.dumps(
            {
                "message": "hello world",
            }
        ),
    }

デプロイ

aws s3 cp \
    api.yaml \
    s3://cm-fujii.genki-deploy/ApiGateway-Need-API-Key-Sample/api.yaml

sam package \
    --output-template-file packaged.yaml \
    --s3-bucket cm-fujii.genki-deploy

sam deploy \
    --template-file packaged.yaml \
    --stack-name ApiGateway-Need-API-Key-Sample-Stack \
    --s3-bucket cm-fujii.genki-deploy \
    --capabilities CAPABILITY_NAMED_IAM \
    --no-fail-on-empty-changeset

APIの動作を確認する

「HEADER」を設定しているため、ヘッダーの「x-api-key」にAPIキーを設定します。

APIキーが無い場合は、401が返ってくる

$ curl -X GET -I https://xxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello

HTTP/2 401

APIキーがある場合(違う)は、401が返ってくる

$ curl -X GET -I https://xxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello \
    -H "x-api-key: invalid-api-key"

HTTP/2 401

APIキーがある場合(合っている)は、200が返ってくる

$ curl -X GET -I https://xxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello \
    -H "x-api-key: valid-api-key"

HTTP/2 200

参考