Serverless custom authorizer issues on AWS.

Will Bowman
asked.io
Published in
2 min readFeb 16, 2018

If you google around you’ll find plenty of examples on how-to create a custom authorizer for serverless lambda events.

I used this example when I was building mine:

// Return an IAM policy document for the current endpoint   
const effect = isAllowed ? 'Allow' : 'Deny';
const userId = user.username;
const authorizerContext = { user: JSON.stringify(user) };
const policyDocument = utils.buildIAMPolicy(userId, effect, event.methodArn, authorizerContext);

I ran into a couple issues after deploying and thought I might share them.

User is not authorized to access this resource

{
“Message”: “User is not authorized to access this resource”
}

After some debugging I noticed the error was not coming from my authorizer but from AWS itself and after redeploying it would go away. I could hit 1 route but not another after.

A bit of googling later I ran across this post that explains what I was seeing:

You may be seeing cached results. The policy generated by the Lambda function should apply to the entire API (all resources/methods), and caching is enabled by default with a TTL of 300 seconds.

Basically the cache key is made up of (token + restApi + authorizer + stage + deployment).

The problem

All of these examples are sending event.methodArn for your policies resource.

This includes the method and route:

arn:aws:execute-api:us-1:abc:123/prod/POST/v1/dinosaurs

Since your cache key does not take your method or route into consideration your authorizer will be used on other methods and routes. Since those are not validated with methodArn the user will be denied access with “User is not authorized to access this resource”.

The solution

Instead of sending event.methodArn you can send a wildcard as noted in the article:

context.succeed(generatePolicy('user', 'Allow', '*');

Or build out your policy:

context.succeed(generatePolicy('user', 'Allow', [
'arn:aws:execute-api:us-1:abc:123/prod/POST/v1/dinosaurs',
'arn:aws:execute-api:us-1:abc:123/prod/GET/v1/dinosaurs',
]);

I’d rather keep users locked down to the specific API so I’d generate something like this from env variables:

context.succeed(generatePolicy('user', 'Allow', 'arn:aws:execute-api:us-1:abc:123/prod/*');

Or something more dynamic:

event.methodArn.split('/').slice(0, 2).join('/') + '/*'

“message”: null

When using an invalid, or missing token AWS kept reporting:

{
“message”: null
}

The problem

Debugging locally with serverless-offline I can see in the console:

Serverless: Running Authorization function for get /user (λ: auth)
Unauthorized
Serverless: Authorization response did not include a principalId: (λ: auth)

This is related to the examples sending Unauthorized as the error to the callback.

callback('Unauthorized');

Serverless responds correctly, with a 401, but in deployment it does not work.

The solution

Denying access to the resource got rid of the errors on AWS and serverless-offline. While I would prefer to receive a 401 response the 403 will have to do for now.

context.succeed(generatePolicy('user', 'Deny', event.methodArn);

--

--