Serverless custom authorizer issues on AWS.

Will Bowman
Feb 16, 2018 · 2 min read

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);

asked.io

A day-to-day log of the questions we’ve asked & the answers…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store