Search code examples
amazon-web-servicesaws-lambdaauthorizationserverless

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


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?


Solution

  • 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!