Build a AWS Custom Authorizer in Python for AppSync
Hello everyone,
It’s been a long time since my last post. I always promise to write with higher frequency but somehow I always get lazy about it. Maybe I should fix this bad habit of mine and periodically post something. Yet I think I should post only when I have something to say so I guess I’ll be stuck in this limbo.
Anyway, at my SRE job as Consultant for Generali Investments, we needed to develop a Custom Authorizer for our lovely Cloud Data Platform, AWS and Python-based, to replace the Cognito Authorizer that doesn’t cover what we need.
A Lambda authorizer is useful if you want to implement a custom authorization scheme that uses a bearer token authentication strategy such as OAuth or SAML, or that uses request parameters to determine the caller’s identity.
On the internet there’s plenty of similar ways to implement a Lambda Authorizer but none was quite specific to our use case. Our APIs are located on API Gateway, they send REST Requests with GraphQL payload to Appsync, passing some custom headers which calls a landing Lambda Function to do some foo() and bar(). With the custom headers passed downstream, we do some specific security operations (mainly row-level-security).
So I’m here writing this article to help you young padawans to get it done.
High Level Architecture
There you go a simple yet fancy architecture. The interactions are the following:
- The client (in my case, our webapp) sends a GET request with a Authorization token that containes a JWT Token.
- The request goes straight to the API Gateway endpoint, stopping by the Custom Authorizer.
The Authorizer, a custom lambda function written in Python:
- decodes the jwt token (unverified claims);
- verifies the claims of the token;
- logs some basic data about the authorization request on CloudWatch.
- responds with an inline policy do Allow/Deny the ‘invoke:’ of the resource.
If allowed, an AppSync endpoint is called that calls a destination lambda as its Data Source.
JWT Token Format
FYI, that’s the structure of a JWT Token. On the right some of the attrs like “exp” “iss” “token_use” will be used to verify the claims of the token with the Custom Authorizer.
API Gateway
Authorizer
First things first, you need to create a Authorizer on API Gateway for your API. You can do it clicking on the sidebar and going to the Authorizer Tab.
Fill it with the following information and, of course, customize it for your setup. Just a quick note if it’s not clear enough for some of you: under “Lambda Function” you need to pick the Custom Authorizer Lambda Function, which will be the Lambda that handles the Authorization part of the workflow. So, if you don’t have it, go create it!
You can now test (actually not) your Authorizer clicking on “Test”. Before doing that, you need to code your Lambda Function.
After inserting the JWT Token, the response should be something like that:
As you can see, it’s a inline policy that will give permission to invoke the Landing Lambda Function.
AppSync
Configure your REST request to contact AppSync in the following way. You can pick the AWS Subdomain Field from your AppSync API.
Under HTTP Headers, insert all the headers you need to pass downstreams to the landing lambda function.
AWS Lambda: Custom Authorizer
So far, so good. I can already smell the success. Now the funny part, the code of our Custom Authorizer.
The code is divided in two parts:
- main.py will handle the general workflow of the lambda.
- jwt_verifier.py will handle the verification of the jwt token. This part of code was taken from AWS Github Repository. Although, I’ve made small adjustments to adapt it and to generalize it for my use case.
The github code is available here. Explanations are in the code itself.
main.py
jwt_verifier.py
AWS Lambda: landing Lambda Function
The landing lambda function is quite simple, it only reads the headers passed by the appsync request.
Conclusion
Now you should be able to create custom authorizations based on information passed by the authentication phase of your workflow. I needed some time to figure all this out. If it helped only one of you, I’d be happy to have written this down.
As usual, reach out and happ developing ❤.