3
votes

I've implemented a custom 'REQUEST' type authorizer for an API gateway which validates a JWT token passed in the 'Authorization' header. I've tested the lambda independently and it works as expected. I've also attached the authorizer to my routes and I can test it in the AWS console - again, everything seems to work (see image):

successful invoke via console

However, when I try to invoke my endpoints with the token in the Authorization header, I always receive an UNAUTHORIZED response:

{
  "errors": [
    {
      "category": "ClientError",
      "code": "UNAUTHORIZED",
      "detail": "Unauthorized",
      "method": "GET",
      "path": "/cases",
      "requestId": "004eb254-a926-45ad-96a5-ce3527621c81",
      "retryable": false
    }
  ]
}

From what I have gathered, API gateway never invokes my Authorizer as i don't see any log events in its cloudwatch. I was able to enable cloudwatch logging of my API gateway, and the only log information I see is as follows:

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|   timestamp   |                                                                                                                             message                                                                                                                              |
|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1578275720543 | (dac0d4f6-1380-4049-bcee-bf776ca78e5c) Extended Request Id: F2v9WFfiIAMF-9w=                                                                                                                                                                                     |
| 1578275720543 | (dac0d4f6-1380-4049-bcee-bf776ca78e5c) Unauthorized request: dac0d4f6-1380-4049-bcee-bf776ca78e5c                                                                                                                                                                |
| 1578275720543 | (dac0d4f6-1380-4049-bcee-bf776ca78e5c) Extended Request Id: F2v9WFfiIAMF-9w=                                                                                                                                                                                     |
| 1578275720544 | (dac0d4f6-1380-4049-bcee-bf776ca78e5c) Gateway response type: UNAUTHORIZED with status code: 401                                                                                                                                                                 |
| 1578275720544 | (dac0d4f6-1380-4049-bcee-bf776ca78e5c) Gateway response body: {"errors": [{"category": "ClientError","code": "UNAUTHORIZED","detail": "Unauthorized","method": "GET","path": "/cases","requestId": "dac0d4f6-1380-4049-bcee-bf776ca78e5c","retryable": false }]} |
| 1578275720544 | (dac0d4f6-1380-4049-bcee-bf776ca78e5c) Gateway response headers: {}                                                                                                                                                                                              |
| 1578275720544 | (dac0d4f6-1380-4049-bcee-bf776ca78e5c) Gateway response type: UNAUTHORIZED with status code: 401                                                                                                                                                                 |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

At this point I am completely stuck and not sure how to debug this further. I'm assuming something must be configured wrong but the log information I can find doesn't given any indication of what the problem is. I've also pasted a copy of my authorizers configuration in the image below:

Authorizer Configuration

Screenshot of one endpoint configured to use the authorizer

1
Please post your authorizer code. I think that's where the problem is.Arun K
@ArunK i've added my authorizer handler to the post, but I'm confused how the code could be the problem here. As I said in my original post, The authorizer works when i invoke it as a lambda and also when I test it from the API gateway authorizer console, and it is never invoked when i make a request to the endpoints via Curl or postman.BruceK
are you using serverless framework. i dont think so ?Arun K
did you confgure the authorizer lambda as the authorizer to any apiArun K
I am using serverless framework but I am manually deploying the shared authorizer so that I can get around the limitation here: serverless.com/framework/docs/providers/aws/events/apigateway/… I can post the cloudformation if you think it is relevant. I've attached the authorizer to many of my endpoints and they all show the same behavior.BruceK

1 Answers

4
votes

I figured out the problem I was having: I needed to set identitySource: method.request.header.Authorization in the authorizer field of the endpoint as well as in the CF stack.

Custom Authorizer definition in raw cloudformation:

service: 
  name: api-base

frameworkVersion: ">=1.2.0 <2.0.0"

plugins:
  - serverless-plugin-optimize
  - serverless-offline
  - serverless-pseudo-parameters
  - serverless-domain-manager

custom:
  stage: ${self:provider.stage, 'dev'}
  serverless-offline:
    port: ${env:OFFLINE_PORT, '4000'}
  false: false
  cognitoStack: marley-auth
  customDomain:
    domainName: ${env:BE_HOST, ''}
    enabled: ${env:EN_CUSTOM_DOMAIN, self:custom.false}
    stage: ${self:provider.stage, 'dev'}
    createRoute53Record: true

provider:
  name: aws
  runtime: nodejs10.x
  versionFunctions: true
  apiName: public
  logs:
    restApi: true
  stackTags:
      COMMIT_SHA: ${env:COMMIT_SHA, 'NO-SHA'}
  environment:
    USER_POOL_ID: ${cf:${self:custom.cognitoStack}-${self:custom.stage}.UserPoolId}
    CLIENT_ID: ${cf:${self:custom.cognitoStack}-${self:custom.stage}.UserPoolClientId}
  timeout: 30
  iamRoleStatements:
  - Effect: "Allow"
    Action:
      - "lambda:InvokeFunction"
    Resource: "*"

functions:
  authorizer:
    handler: handler/authorize.handler

resources:
  - Outputs:
      ApiGatewayRestApiId:
        Value:
          Ref: ApiGatewayRestApi
        Export:
          Name: ${self:custom.stage}-${self:provider.apiName}-ApiGatewayRestApiId
      ApiGatewayRestApiRootResourceId:
        Value:
          Fn::GetAtt:
            - ApiGatewayRestApi
            - RootResourceId 
        Export:
          Name: ${self:custom.stage}-${self:provider.apiName}-ApiGatewayRestApiRootResourceId
      SharedAuthorizerId:
        Value:
          Ref: SharedAuthorizer
        Export:
          Name: ${self:custom.stage}-${self:provider.apiName}-ApiGatewaySharedAuthorizerId
  - Resources:
      SharedAuthorizer:
        Type: AWS::ApiGateway::Authorizer
        Properties:
          Name: public
          AuthorizerUri: !Join 
            - ''
            - - 'arn:aws:apigateway:'
              - !Ref 'AWS::Region'
              - ':lambda:path/2015-03-31/functions/'
              - !GetAtt 
                - AuthorizerLambdaFunction
                - Arn
              - /invocations
          RestApiId: !Ref 'ApiGatewayRestApi'
          Type: REQUEST
          IdentitySource: method.request.header.Authorization
          AuthorizerResultTtlInSeconds: '300'
        DependsOn: AuthorizerLambdaFunction
      ApiAuthLambdaPermission:
        Type: AWS::Lambda::Permission
        Properties:
          Action: lambda:InvokeFunction
          FunctionName: !Ref AuthorizerLambdaFunction
          Principal: apigateway.amazonaws.com
          SourceArn: !Sub "arn:aws:execute-api:#{AWS::Region}:#{AWS::AccountId}:#{ApiGatewayRestApi}/authorizers/*"
        DependsOn: ApiGatewayRestApi

Using the authorizer in another stack - note that I have specified IdentitySource here as well as in the definition of the authorizer - for some reason I had to do it in both places.

authorizer:
  type: CUSTOM
  authorizerId: ${cf:api-base-${self:custom.stage}.SharedAuthorizerId}
  identitySource: method.request.header.Authorization