API Gateway Responses and Handling Authorizer Responses

Intro

I have added a token-based Lambda Authorizer for all my backend services deployed on API Gateway. I wanted the user to be signed out if the authorizer responds with a 401 (Unauthorized) or 403 (Access Denied) error.

This is how my code looked like for the authorizer lambda:

const jwt = require('jsonwebtoken');

exports.handler = function (event, context, callback) {
  const token = event.authorizationToken?.split(' ')[1];

  if (!token) {
    callback('Unauthorized'); // Return a 401 Unauthorized response
  }

  const data = jwt.verify(token, process.env.SECRET, (err, data) => {
    if (err) {
            // Return a 403 Access Denied response
      callback(null, generatePolicy('user', 'Deny', event.methodArn, {"messageString":"Invalid token"}));
    }
    return data;
  });

  callback(null, generatePolicy('user', 'Allow', event.methodArn, data));
};

// Help function to generate an IAM policy
const generatePolicy = function (principalId, effect, resource, data) {
  const authResponse = {};

  authResponse.principalId = principalId;
  if (effect && resource) {
    const policyDocument = {};
    policyDocument.Version = '2012-10-17';
    policyDocument.Statement = [];
    const statementOne = {};
    statementOne.Action = 'execute-api:Invoke';
    statementOne.Effect = effect;
    statementOne.Resource = resource;
    policyDocument.Statement[0] = statementOne;
    authResponse.policyDocument = policyDocument;
  }

  // Optional output with custom properties of the String, Number or Boolean type.
  authResponse.context = {
    ...data,
  };
  return authResponse;
};

After this, I used to get error logs on my client apps, but I could not access the response object to check the response's status code.

I needed to determine if an error was caused by my authorizer (resulting in a 401 or 403 status code) or by my API endpoint lambdas. The issue was that the response was being caught as a fetch error in the catch block instead of returning a response with the expected status code and message that I could handle properly.

Additionally, I guess it was even giving me a CORS error, which I wasn't able to understand why.

Solution

After quite a bit of searching, I found out that to receive proper handleable responses, I would need to configure the API Gateway Responses section in the API Gateway.

gateway responses

Here, you can see all of the responses the API Gateway is capable of handling.

Add the CORS headers in the response header to receive proper handleable responses and configure the response templates as you need.

In my case, this is how I configured my Access Denied:

access_denied api gateway response config

#Note: Here, the messageString comes from the context data that we are sending from our Authorizer function that we defined previously.

.
.
.
if (err) {
    // Return a 403 Access Denied response
  callback(null, generatePolicy('user', 'Deny', event.methodArn, {"messageString":"Invalid token"}));
}
.
.
.
  authResponse.context = {
    ...data,
  };
}

For more info:

https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-gatewayResponse-definition.html#api-gateway-gatewayResponse-definition