2

We have an API Gateway utilising a custom Token authoriser. We have 2 lambdas - Greetings and GenerateToken.

We only want the Greetings lambda to be behind a authoriser - requires to be called in the following way utilising SAM:

curl -X GET \
  https://<apigatewayid>.execute-api.eu-west-1.amazonaws.com/Prod/generateToken \
  -H 'X-API-KEY: allow'

How can we achieve that the GenerateToken path does not require a HTTP token for authenticating?

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: API Gateway with Lambda Token Authorizer
Resources:
  GreetingsApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      DefinitionBody:
        swagger: 2.0
        x-amazon-apigateway-policy:
          Version: "2012-10-17"
          Statement:
            - Effect: Allow
              Principal: "*"
              Action: execute-api:Invoke
              Resource:
                - execute-api:/*/*/*
        paths:
          "/hello":
            get:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GreetingsLambda.Arn}/invocations
              responses: {}
          "/generateToken":
            get:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GenerateTokenLambda.Arn}/invocations
              responses: {}
      Auth:
        DefaultAuthorizer: CustomAuthorizer
        Authorizers:
          MyAuthorizer:
            FunctionArn: !GetAtt AuthLambda.Arn
            Identity:
              Header: X-API-KEY

  GenerateTokenLambda:
    Type: AWS::Serverless::Function
    Properties:
      Role: !GetAtt LambdaRole.Arn
      CodeUri: "s3://<bucket-name>/code.zip"
      Handler: src/generateToken.handler
      Events:
        GetRoot:
          Type: Api
          Properties:
            RestApiId: !Ref GreetingsApiGateway
            Path: /generateToken
            Method: get

  GreetingsLambda:
    Type: AWS::Serverless::Function
    Properties:
      Role: !GetAtt LambdaRole.Arn
      CodeUri: "s3://<bucket-name>/code.zip"
      Handler: src/greetings.handler
      Events:
        GetRoot:
          Type: Api
          Properties:
            RestApiId: !Ref GreetingsApiGateway
            Path: /hello
            Method: get

  AuthLambda:
    Type: AWS::Serverless::Function
    Properties:
      Role: !GetAtt LambdaRole.Arn
      CodeUri: "s3://<bucket-name>/code.zip"
      Handler: src/auth.handler

Globals:
  Function:
    Runtime: nodejs8.10

Outputs:
  ApiURL:
    Description: "OUR API URL"
    Value: !Sub "https://${GreetingsApiGateway}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
Igor L.
  • 3,159
  • 7
  • 40
  • 61

3 Answers3

2

I am not very sure if I completely understood what you want, but here's a Cloudformation template to create api-gateway resources with & without authorization enabled. I am using Cognito User Pool authorization method but it can be just as easily be a custom authorizer.

RestAPI:
  Type: AWS::ApiGateway::RestApi
  DeletionPolicy: Delete
  Properties:
    Name: {"Ref": "AWS::StackName"}
    ApiKeySourceType: HEADER
    EndpointConfiguration:
      Types:
        - EDGE

ApiAuthorizer:
  Type: AWS::ApiGateway::Authorizer
  DeletionPolicy: Retain
  DependsOn: UserPoolList
  Properties:
    Name: !Join ["-", [{"Ref": "AWS::StackName"}, "authorizer"]]
    RestApiId: !Ref RestAPI
    Type: COGNITO_USER_POOLS
    AuthType: cognito_user_pools
    IdentitySource: "method.request.header.Authorization"
    ProviderARNs: <User Pool ARN>

ResourceSignin:
  Type: AWS::ApiGateway::Resource
  DeletionPolicy: Delete
  Properties:
    RestApiId: !Ref RestAPI
    ParentId: !GetAtt RestAPI.RootResourceId
    PathPart: "signin"

SigninPostMethod:
  Type: AWS::ApiGateway::Method
  Properties:
    RestApiId: !Ref RestAPI
    ResourceId: !Ref ResourceSignin
    HttpMethod: POST
    AuthorizationType: NONE
    ApiKeyRequired: <true/false>
    Integration:
      Type: AWS_PROXY
      IntegrationHttpMethod: POST
      Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${UserHandlerFunction.Arn}/invocations
      Credentials: !GetAtt GenApiGatewayRole.Arn

ResourceUserCreate:
  Type: AWS::ApiGateway::Resource
  DeletionPolicy: Delete
  Properties:
    RestApiId: !Ref RestAPI
    ParentId: !GetAtt RestAPI.RootResourceId
    PathPart: "create"

CreatePostMethod:
  Type: AWS::ApiGateway::Method
  Properties:
    RestApiId: !Ref RestAPI
    ResourceId: !Ref ResourceUserCreate
    HttpMethod: POST
    AuthorizationType: COGNITO_USER_POOLS
    AuthorizerId: !Ref ApiAuthorizer
    ApiKeyRequired: <true/false>
    Integration:
      Type: AWS_PROXY
      IntegrationHttpMethod: POST
      Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${UserHandlerFunction.Arn}/invocations
      Credentials: !GetAtt UserApiGatewayRole.Arn

Here resource signin has a POST method with no authorization while create resource has a POST method with authorization enabled.

If you are planning to use API keys this might be the only way possible. I could not get API keys to work with SAM (I believe API keys with SAM is not yet supported - this was about a month back, but you can double check).

asr9
  • 2,440
  • 1
  • 21
  • 37
  • Thanks for this answer, I failed to make it work within our SAM template - my question is more related to SAM and Token authorisers. Updated the question in an attempt to make it clearer – Igor L. Mar 21 '19 at 10:51
  • You say `GreetingsLambda` is to be behind an authorizer but the `curl` example has a call to `generateToken` with `X-API-KEY: allow`. Is that header for authorizer or are you using `API keys`? – asr9 Mar 21 '19 at 13:35
  • thats the header for authorizer – Igor L. Mar 21 '19 at 13:40
0

We can achieve this via swagger within API Gateway:

The World lambda is a public API and the Hello lambda resides behind the AuthLambda authorizer


  OurApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      Auth:
        Authorizers:
          MyAuthorizer:
            FunctionPayloadType: REQUEST
            FunctionArn: !GetAtt AuthLambda.Arn
      DefinitionBody:
        swagger: 2.0
        basePath: /prod
        info:
          title: AwsSamExample
        x-amazon-apigateway-policy:
          Version: "2012-10-17"
          Statement:
            - Effect: Allow
              Principal: "*"
              Action: execute-api:Invoke
              Resource:
                - execute-api:/*/*/*
        schemes:
          - https
        paths:
          "/hello":
            get:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HelloLambda.Arn}/invocations
              responses: {}
              security:
                - MyAuthorizer: []
          "/world":
            get:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${WorldLambda.Arn}/invocations
              responses: {}
              security: []


Igor L.
  • 3,159
  • 7
  • 40
  • 61
0

Use the Serverless framework and easily manage this with just an attribute. https://www.npmjs.com/package/serverless

Cleriston
  • 750
  • 5
  • 11