An example serverless web application using Flask and AWS Cognito with JSON Web Tokens (JWT) to protect specific routes, powered by API Gateway and Lambda.
A high level overview of how the application works is as follows. The Flask application includes a number of blueprints:
home
: a simple homepage that includes a login link.auth
: login, post-login, and logout routes.private
: routes that are only accessible after a user has logged in. If a user is not logged in these routes automatically redirect to the Cognito hosted UI.
When accessing the /login
route, a user is redirected to the Cognito hosted UI. After successful authentication with the user pool, a JWT is returned to the app through the /postlogin
endpoint which is stored in a httponly cookie; this cookie is valid for 30 mins and used to authorise access to any routes that are protected with the @auth_required
decorator. The app itself uses flask-cognito-lib
for the heaving lifting of validating JWTs and protecting specific routes.
For the deployment side, CDK python code is provided in /deploy
. The CDK stacks will deploy the Flask application as a docker container to Lambda, with API Gateway in front. A Cognito User Pool is created along with a User Pool Client for the Flask application.
Prequisites:
- poetry
- pre-commit
- AWS CDK (v2.15.0)
Setup as follows:
# setup the python environment with poetry
make install
# Populate the .env file the required parameters
cp .env.example .env
vi .env
# deploy the infra (Cognito, API Gateway, Lambda)
cd deploy
cdk deploy --all
# Populate AWS_COGNITO user pool parameters after deploying
# Obtain these values from the Systems Manager Parameter Store
vi .env
# run locally using Workzeug
make local # or go to the AWS API Gateway URL
# check other useful commands
make help
The Makefile includes helpful commands for setting up a development environment, get started by installing the package into a new virtual environment and setting up pre-commit with make install
. Run make help
to see additional available commands (e.g. linting, testing, docker, and so on).
The application can be run locally through docker (make docker-build && make docker-run
) or from the installed virtualenv with make local
. The app should launched at http://localhost:5000 and a login link should redirect you to the Cognito hosted UI to sign up / sign in. Once logged in, a cookie will store the JWT and you will be redirected to the /private
page. You may also view details of the JWT at the /token
endpoint.
- Pytest is used for the functional tests of the application (see
/tests
). - Code is linted using flake8
- Code formatting is validated using Black
- pre-commit is used to run these checks locally before files are pushed to git
- The Github Actions pipeline also runs these checks and tests
- CORS
- Serverless deployment with AWS CDK
- Logout route should also hit the revoke Cognito endpoint
- Handle JWT authenticated routes in local development (i.e. no Cognito) -> Set
AWS_COGNITO_DISABLED
toTrue
- Populate parameters from SSM at runtime rather than deploy time
This project was inspired by the blog post Integrating Cognito with Flask by Martin Campbell.