2

I've managed to deploy an AWS HTTP API with differents routes and lambda integrations via yaml SAM template with OpenAPI definition but I'm stuck with adding custom lambda authorizer to my routes. When I deploy the stack I got some timeout for API creation :

ROLLBACK_IN_PROGRESS                     AWS::CloudFormation::Stack               CloudArYer                               The following resource(s) failed to
                                                                                                                           create: [Api]. . Rollback requested by
                                                                                                                           user.
CREATE_FAILED                            AWS::ApiGatewayV2::Api                   Api                                      Internal server error (Service:
                                                                                                                           AmazonApiGatewayV2; Status Code: 500;
                                                                                                                           Error Code: InternalServerException;
                                                                                                                           Request ID: 18242cfd-
                                                                                                                           cc94-4909-a26a-6631806f94e7; Proxy:
                                                                                                                           null)

here is the main part of my template with API definition :

...
  AuthorizerLambdaTemplate:
    Type: AWS::Serverless::Application
    Properties:
      Location: ./templates/Authorizer-template-function.yaml
      Parameters:
        ProjectName: !Sub "${ProjectName}"
        ProjectApiKey: !Sub "${ProjectApiKey}"

  Api:
    Type: AWS::Serverless::HttpApi
    Properties:
      StageName: CloudArYerAPI
      CorsConfiguration:
        AllowCredentials: true
        AllowHeaders: "*"
        AllowMethods:
          - GET
          - POST
          - PUT
        AllowOrigins:
          - https://*
      DefinitionBody:
        openapi: 3.0.1
        info:
          title: CoudArYer-API
          description: HTTP API for connected chicken coop (Cloud Ar Yer)
          version: 2020-09-26
        paths:
          /config/{device}:
            get:
              x-amazon-apigateway-integration:
                $ref: "#/components/x-amazon-apigateway-integrations/GETLambda"
          /event/{type}:
            post:
              x-amazon-apigateway-integration:
                $ref: "#/components/x-amazon-apigateway-integrations/POSTLambda"
          /image/{origin}/{device}:
            put:
              x-amazon-apigateway-integration:
                $ref: "#/components/x-amazon-apigateway-integrations/PUTLambda"

        security:
          - CloudArYer-Authorizer: []

        components:
          securitySchemes:
            CloudArYer-Authorizer:
              type: apiKey
              name: authorization
              in: header
              x-amazon-apigateway-authtype: custom
              x-amazon-apigateway-authorizer:
                type: request
                identitySource: $request.header.authorization
                authorizerUri: 
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AuthorizerLambdaTemplate.Outputs.FunctionArn}/invocations
                authorizerCredentials: !GetAtt ApiGatewayAuthorizerRole.Arn
                authorizerPayloadFormatVersion: "2.0"
                authorizerResultTtlInSeconds: 60
                enableSimpleResponses: true

          x-amazon-apigateway-integrations:
            PUTLambda:
              payloadFormatVersion: "2.0"
              type: "aws_proxy"
              httpMethod: "POST"
              uri:
                Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PUTImageLambdaTemplate.Outputs.FunctionArn}/invocations
              connectionType: "INTERNET"
            GETLambda:
              payloadFormatVersion: "2.0"
              type: "aws_proxy"
              httpMethod: "POST"
              uri:
                Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GETConfigLambdaTemplate.Outputs.FunctionArn}/invocations
              connectionType: "INTERNET"
            POSTLambda:
              payloadFormatVersion: "2.0"
              type: "aws_proxy"
              httpMethod: "POST"
              uri:
                Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${POSTEventLambdaTemplate.Outputs.FunctionArn}/invocations
              connectionType: "INTERNET"

  ApiGatewayAuthorizerRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - Effect: "Allow"
            Principal: 
              Service: 
                - "apigateway.amazonaws.com"
            Action: 
              - sts:AssumeRole
      Policies: 
        - PolicyName: "InvokeAuthorizerFunction"
          PolicyDocument: 
            Version: "2012-10-17"
            Statement: 
              - Effect: "Allow"
                Action:
                  - lambda:InvokeAsync
                  - lambda:InvokeFunction
                Resource: !GetAtt AuthorizerLambdaTemplate.Outputs.FunctionArn
...

My authorizer lambda is define in a nested stack (AuthorizerLambdaTemplate)

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  AuthorizerFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub "${ProjectName}-Authorizer-Lambda"
      CodeUri: ../src/authorizer
      Handler: handler.authorizer
      Runtime: nodejs10.x
      Role: !Sub "${CustomAuthorizerFunctionRole.Arn}"
      Environment:
        Variables:
          ProjectApiKey: !Sub "${ProjectApiKey}"

  CustomAuthorizerFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal: 
              Service: 
                - "lambda.amazonaws.com"
            Action: 
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

Parameters:
  ProjectName: 
    Type: String
  ProjectApiKey:
    Type: String

Outputs:
  FunctionArn:
    Description: Arn Authorizer function
    Value: !GetAtt AuthorizerFunction.Arn

and the code for lambda is defined as follow in an extenal directory

exports.authorizer = async(event) => {
    let response = {
        "isAuthorized": false,
        "context": {
            "stringKey": "test"
        }
    };

    if (event.headers.authorization === process.env.ProjectApiKey) {
        response = {
            "isAuthorized": true,
            "context": {
                "stringKey": "test"
            }
        };
    }

    return response;
};

I do not understand why the deployment is blocked on the API creation ... and failed with an InternalServerException. Where am I wrong on the stack definition. I've browsed many sites, snippets... but less info for new HTTP API and for instance no clue to fix my issue.

Thanks for your potential help ! :-)

1 Answer 1

1

You can add Lambda authorizers with just SAM, and the serverless functions can reference the authorizer. The cloudformation for the authorizer should look something like this:

LambdaAuthorizer:
    Type: 'AWS::ApiGatewayV2::Authorizer'
    Properties:
         Name: LambdaAuthorizer
         ApiId: !Ref HttpApi
         AuthorizerType: REQUEST
         AuthorizerUri: arn:aws:apigateway:{region}:lambda:path/2015-03-31/functions/arn:aws:lambda: {region}:{account id}:function:{Function name}/invocations
         IdentitySource:
             - $request.header.Authorization
         AuthorizerPayloadFormatVersion: 2.0

TestGET:
    Type: AWS::Serverless::Function
    Properties:
        CodeUri: .
        Handler: items.get
        Runtime: nodejs12.x
        Events:
        GetAPI:
            Type: Api
            Properties:
                Auth:
                    Authorizer: LambdaAuthorizer
                RestApiId: !Ref Api
                Path: /items
                Method: get

Documentation for Lambda Authorizers with HTTP APIs: https://aws.amazon.com/blogs/compute/introducing-iam-and-lambda-authorizers-for-amazon-api-gateway-http-apis/

If you still desire to use the open api spec, the yaml structure you have is incorrect. To add security via the open api spec, it should look like this:

securityDefinitions:
    LambdaAuthorizer:
        type: "apiKey"
        name: "Authorization"
        in: "header"
        x-amazon-apigateway-authtype: "custom"
        x-amazon-apigateway-authorizer:
            authorizerUri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${LambdaAuthorizer}/invocations"
            authorizerResultTtlInSeconds: 300
            identitySource: "method.request.header.Authorization"
            type: "request"

Note: This does not live under components. It should live under the top level DefinitionBody.

Documentation for Custom Authorizer in Definition Body: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions-authorizer.html

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.