Boilerplate example for setting up a FastAPI endpoint with AWS Cognito authorisation.
Provides run scripts to bundle a Docker image and upload to ECR for later deployment.
Setup loading .env variables: https://direnv.net/
direnv allow
Uses Python 3.11. To easily switch between versions of python, consider setting up pyenv.
PDM is used for proper dependency resolution and convenience scripts.
pip install pipx
pipx install pdm
Pre-commit is used for some commit hooks:
pip install pre-commit
pre-commit install
- Enable IAM Identity Center following: https://aws.amazon.com/iam/identity-center/
- Add user
admin
- Add group
Admins
to Groups - Add
admin
toAdmins
- Add a permission set
AdministratorAccess
based on theAdministratorAccess
AWS managed policy - Assign the permission set to the
Admins
group underAWS accounts → Assign users or groups
-
Install AWS CLI
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip awscliv2.zip sudo ./aws/install # or update: $ sudo ./aws/install --bin-dir /usr/local/bin --install-dir /usr/local/aws-cli --update
(ref: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
-
Configure CLI session and login
aws configure sso # enter info... aws sso login --sso-session admin
-
Check the resulting config at
~/.aws/config
and make sure it matches what you expect, for ex:[profile admin] region = us-east-1 sso_session = admin sso_account_id = 123456789012 sso_role_name = AdministratorAccess [sso-session admin] sso_region = us-east-1 sso_start_url = https://my-sso-portal.awsapps.com/start sso_registration_scopes = sso:account:access
(ref: https://docs.aws.amazon.com/cli/latest/userguide/sso-configure-profile-token.html)
You'll need to setup an authentication provider for the FastAPI endpoints.
The FastAPI authentication library used is fastapi-cloudauth, which supports AWS Cognito / Auth0 / Firebase Auth. The app is setup to use Cognito.
- Create a new user pool in Cognito in the AWS console.
- Sign-in and sign-up options should be arbitrary, just don't enable public sign-up. You don't need any actual users or groups.
- Once the user pool is created, select it and click "App integration" options.
- Under "Domain" setup a new cognito domain
- Create a resource server, with Resource server identifier
pyapi
and custom scopeuser
for example. These values should match.env
environment variableCOGNITO_AUTH_USER_SCOPE
, i.e. as inpyapi/user
(resource_server_id/custom_scope_name). - Under "App client list" click "Create app client" → App type "Confidential client"
- Client secret → Generate a client secret
- Accept default and create
- Once created, select the app client again and configure Hosted UI
- Callback URLs: http://localhost
- OAuth grant types: enable Client credentials grant
- Custom scopes:
pyapi/user
(for example)
Once this setup is complete, note your:
- Custom Cognito user pool domain
- Custom scope
- App integration → app client → Client ID
- App integration → app client → Client secret
These will be needed to generate access tokens to authenticate requests against the api later.
pdm install-all
pdm docker-build
Set environment variables for Dockerfile images in .env.dockerfile
. Make sure to set PYTHON_ENV=production
, or unset it. The app needs some Cognito config to be set in .env.dockerfile
so that it knows what user pool to authenticate against.
Make sure AWS_REGION
and AWS_ACCOUNT_ID
are set in .env
environment variables. These are needed to login Docker to ECR.
Login Docker to AWS ECR:
pdm docker-login
Create a new private registry called fastapi
(adjust the PDM run scripts in pyproject.toml
to change this): https://console.aws.amazon.com/ecr/get-started
Push the built docker image to ECR:
pdm docker-push
No infrastructure is provisioned in code for this example.
An example deploying on Fargate:
- Create a new ECS cluster, type Fargate.
- Create an
ecsTaskExecutionRole
following: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html#create-task-execution-role - Attach the following inline policy to
ecsTaskExecutionRole
(in addition to AmazonECSTaskExecutionRolePolicy):{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup" ], "Resource": "*" } ] }
- Create a new task definition from JSON file
task-definition.json
, editing the file with theecsTaskExecutionRole
's ARN. - In the ECS cluster, create a new service:
- Specify FARGATE_SPOT capacity provider
- Select Task definition → Family: pyapi
- Service name: fastapi
- Edit security group config at: AWS ECS → Clusters → <cluster_name> → Services → <service_name> → Configuration and networking → Network configuration → Security groups
- Edit inbound rules → Add new custom TCP inbound rule for port 5000, source "My IP" (or anywhere)
- Grab the endpoint's Public ID from Tasks → <task_id> → Public IP
Note: memory requirements for each task varies depending on what kind of model and job you're running! Check the task definition JSON file to adjust: task-definition.json
.
Different pdm scripts exist for testing the endpoint locally:
- asgi: run the FastAPI app via an ASGI server, without docker
- docker-run fastapi: run the FastAPI app via the docker image
In order to authenticate against the FastAPI endpoints, you need to provide credentials. This section assumes a Cognito User Pool and App Client have already been setup previously (see here), and is an example for using Postman from a trusted client to side step having to setup any actual users and login in Cognito.
- Create a new postman environment, and edit it, adding the following variables:
client_id <client_id_from_cognito_app_client>
client_secret <client_secret_from_cognito_app_client>
token_url https://<cognito_subdomain>.<aws_region>.amazoncognito.com/oauth2/token
scope pyapi/user (or whatever scope you wish, i.e. pyapi/write, pyapi/read, ..., matching the values configured in the Cognito App Client)
- Create a new request
- Click Authorization → OAuth 2.0 → Add auth data to Request Headers
- Fill in the Configuration Options:
Token Name pyapi (arbitrary)
Grant Type Client credentials
Access Token URL {{token_url}}
Client ID {{client_id}}
Client Secret {{client_secret}}
Scope {{scope}}
Client Authentication Send client credentials in body (arbitrary)
- Click "Get New Access Token" and copy the access_token in the response body
- Alternatively, you can just directly send a POST request yourself to the endpoint at
{{token_url}}
, specifying ax-www-form-urlencoded
body:
grant_type client_credentials
client_id {{client_id}}
client_secret {{client_secret}}
scope {{scope}}
- Open a new request to the development endpoint at (for ex.)
GET http://localhost:5000/v1/healthz
- Click Authorization → OAuth 2.0 → Add auth data to Request Headers
- Paste the access token into Current Token → Access Token, just above Header Prefix "Bearer"
Make sure to set the .env
environment variables for Cognito.
Start up endpoint:
pdm docker-build
pdm docker-run
Then:
pdm test