Allow Guest Mode (no authorization) in Custom Authorizer in AWS

Viewed 388

I'm trying to find a way to allow guest mode in custom authorizer in AWS.

Basically, what I want to achieve is the following scenario:

  • if there is no Authorization header in request then trigger lambda function that responds with some data
  • if there is Authorization header then my custom authorizer should check JWT token and either Allow or Deny. Then trigger lambda if custom-authorizer returned Allow

I see I can achieve either but not both, i.e. I can open the endpoint (remove authorizer completely) which works okay or I can put authorizer which again works okay.

Yet, I don't see a way to by-pass custom-authorizer when there is no Authorization header.

Example configuration in serverless:

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: /hello
          method: get
          private: true
          authorizer:
            identitySource: method.request.header.Authorization # Can this be optional?
            name: custom-authorizer
  custom-authorizer:
    handler: authorizer.handler

From my testing I can confirm that once authorization is there and there is no Authorization header in request then my custom-authorizer is not triggered at all and API gateway responds straight away with 401 Unauthorized.

Please take into account that in my guest mode I don't want to get custom API gateway response (this is possible and works). I want to trigger lambda function just like there was no authorization at all.

I'm coming to conclusion this is impossible and the only workaround is to remove authorization and then do some custom code in lambda.

Any advice?

2 Answers

If you configure your sls event handler as httpApi*, rather than http, then identitySource is an optional input to authorizer (assuming the serverless api matches the AWS api).

The relevant CloudFormation AWS::ApiGatewayV2::Authorizer docs are here.

As you experienced, if identitySource is configured but not present in the client request, Apig returns 401. If you omit identitySource from the config, all requests will be sent to your authoriser lambda. Your lambda can then handle the three cases: no token, bad token, good token.

* httpApi deploys as ApiGatewayV2 in AWS CloudFormation. V2 is, as the Serverless docs say, "faster and cheaper than v1". The api and feature set differ somewhat between the "HTTP Api" (V2) "Rest Api" (V1), but httpApi is a good default choice.

Following up on @fedonev answer here is the complete solution:

  • Use HTTP API instead of REST API.
  • Update custom-authorizer

Let's start with updating serverless.yml accordingly to the newer and different HTTP API syntax:

Replace events.http with events.httpApi and add httpApi.authorizer to provider. Updated serverless.yml:

provider:
  httpApi:                          # Added
    authorizers:
      customAuthorizer:
        type: request
        functionName: custom-authorizer

functions:
  hello:
    handler: handler.hello
    events:
      - httpApi:                    # Changed
          path: /hello
          method: get
          authorizer:
            name: customAuthorizer  # Changed

custom-authorizer:
  handler: authorizer.handler

Next, we need to update our authorizer handler function as well. It still needs to return policy as it was doing with REST API. Yet... there is no event.authorizationToken and no event.methodArn anymore (I left it commented out for reference):

module.exports.handler = async (event) => {
  if (
    !event.headers.authorization ||
    event.headers.authorization === 'Bearer ABCDEF'
  ) {
    // if (event.authorizationToken === 'Bearer ABCDEF')
    return {
      principalId: 'anonymous',
      policyDocument: {
        Version: '2012-10-17',
        Statement: [
          {
            Action: 'execute-api:Invoke',
            Effect: 'Allow',
            Resource: event.routeArn,
            // Resource: event.methodArn,
          },
        ],
      },
    };
  }
  throw Error('Unauthorized');
};

Now, we can see we have a full control over authorization and we can check if the token is there: !event.headers.authorization (this simulates guest mode) or if token is valid: event.headers.authorization === 'Bearer ABCDEF'. If the token is invalid we throw error which gives 401 Unauthorized as expected.

Finally, worth noting is that all headers keys are lowercased and principalId is mandatory (as previously, when using REST API, authorizer worked without it).

Deploy and enjoy!

Related