|
| 1 | +# Amazon CloudFront signed cookies with Amazon Cognito authentication using Python CDK |
| 2 | + |
| 3 | +This pattern demonstrates how to implement Amazon CloudFront signed cookies for private S3 content access with Amazon Cognito user authentication using AWS CDK with Python. |
| 4 | + |
| 5 | +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. |
| 6 | + |
| 7 | +## Requirements |
| 8 | + |
| 9 | +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. |
| 10 | +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured |
| 11 | +* [AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/cli.html) installed and configured |
| 12 | +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) |
| 13 | +* [Python 3.9+](https://www.python.org/downloads/) installed |
| 14 | +* [Docker] required during the building process |
| 15 | + |
| 16 | +## Deployment Instructions |
| 17 | + |
| 18 | +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: |
| 19 | + ``` |
| 20 | + git clone https://github.com/aws-samples/serverless-patterns |
| 21 | + ``` |
| 22 | +2. Change directory to the pattern directory: |
| 23 | + ``` |
| 24 | + cd cloudfront-s3-signed-cookies-cognito |
| 25 | + ``` |
| 26 | +3. Create a virtual environment for Python: |
| 27 | + ```bash |
| 28 | + python3 -m venv .venv |
| 29 | + ``` |
| 30 | +4. Activate the virtual environment: |
| 31 | + ```bash |
| 32 | + source .venv/bin/activate |
| 33 | + ``` |
| 34 | + For Windows: |
| 35 | + ```bash |
| 36 | + .venv\Scripts\activate.bat |
| 37 | + ``` |
| 38 | +5. Install the required dependencies: |
| 39 | + ```bash |
| 40 | + pip install -r requirements.txt |
| 41 | + ``` |
| 42 | +6. Bootstrap your AWS account for CDK (if you haven't done so already): |
| 43 | + ```bash |
| 44 | + cdk bootstrap |
| 45 | + ``` |
| 46 | +7. Deploy the stack: |
| 47 | + ```bash |
| 48 | + cdk deploy |
| 49 | + ``` |
| 50 | +Note the outputs from the CDK deployment. These contain the API endpoints, CloudFront distribution URL, and other important resource information. |
| 51 | +
|
| 52 | +## How it works |
| 53 | +
|
| 54 | +This pattern creates a secure content delivery solution using CloudFront signed cookies with the following workflow: |
| 55 | +
|
| 56 | +1. **User Registration**: Users register via the `/register` API endpoint, which creates a new user in the Amazon Cognito User Pool. |
| 57 | +
|
| 58 | +2. **User Authentication**: Users authenticate via the `/login` API endpoint with their email and password. Upon successful authentication: |
| 59 | + - Cognito returns JWT tokens (ID token, access token, refresh token) |
| 60 | + - The Lambda function retrieves the CloudFront private key from AWS Secrets Manager |
| 61 | + - CloudFront signed cookies are generated with a configurable TTL |
| 62 | + - Both Cognito tokens and signed cookies are returned to the client |
| 63 | +
|
| 64 | +3. **Content Access**: |
| 65 | + - Public content under the default path is accessible without authentication |
| 66 | + - Private content under the `/private/*` path requires valid CloudFront signed cookies |
| 67 | + - The CloudFront distribution validates the signed cookies against the configured Key Group |
| 68 | +
|
| 69 | +4. **Security**: |
| 70 | + - S3 bucket is configured as private with no public access |
| 71 | + - CloudFront uses Origin Access Control (OAC) to securely access S3 content |
| 72 | + - RSA key pairs are used for signing, with the private key securely stored in AWS Secrets Manager |
| 73 | + - The public key is configured in a CloudFront Key Group for cookie validation |
| 74 | +
|
| 75 | +## Architecture Components |
| 76 | +
|
| 77 | +- **Amazon Cognito User Pool**: Manages user registration and authentication |
| 78 | +- **API Gateway**: REST API with `/register` and `/login` endpoints |
| 79 | +- **AWS Lambda**: Two functions for user registration and login (with signed cookie generation) |
| 80 | +- **AWS Secrets Manager**: Securely stores the CloudFront private key |
| 81 | +- **Amazon S3**: Hosts private content accessible only via CloudFront |
| 82 | +- **Amazon CloudFront**: |
| 83 | + - Distribution with Origin Access Control (OAC) |
| 84 | + - Public Key and Key Group for signed cookie validation |
| 85 | + - Behavior rules for public vs private content |
| 86 | +- **AWS Lambda Powertools**: For structured logging and observability |
| 87 | +
|
| 88 | +## Testing |
| 89 | +
|
| 90 | +### 1. Register a new user |
| 91 | +
|
| 92 | +```bash |
| 93 | +curl -X POST https://{API_ENDPOINT}/register \ |
| 94 | + -H "Content-Type: application/json" \ |
| 95 | + -d '{ |
| 96 | + "email": "user@example.com", |
| 97 | + "password": "TestPassword123!", |
| 98 | + "name": "Test User" |
| 99 | + }' |
| 100 | +``` |
| 101 | + |
| 102 | +### 2. Login and receive signed cookies |
| 103 | + |
| 104 | +```bash |
| 105 | +curl -X POST https://{API_ENDPOINT}/login \ |
| 106 | + -H "Content-Type: application/json" \ |
| 107 | + -c cookies.txt \ |
| 108 | + -d '{ |
| 109 | + "email": "user@example.com", |
| 110 | + "password": "TestPassword123!" |
| 111 | + }' |
| 112 | +``` |
| 113 | + |
| 114 | +The response includes Cognito tokens and CloudFront signed cookies (`CloudFront-Policy`, `CloudFront-Signature`, `CloudFront-Key-Pair-Id`). |
| 115 | + |
| 116 | +### 3. Upload test content to S3 |
| 117 | + |
| 118 | +```bash |
| 119 | +# Upload public content |
| 120 | +aws s3 cp test/public_content.html s3://{BUCKET_NAME}/public_content.html |
| 121 | + |
| 122 | +# Upload private content |
| 123 | +aws s3 cp test/private_content.html s3://{BUCKET_NAME}/private/private_content.html |
| 124 | +``` |
| 125 | + |
| 126 | +### 4. Access public content (no authentication required) |
| 127 | + |
| 128 | +```bash |
| 129 | +curl https://{CLOUDFRONT_DOMAIN}/public_content.html |
| 130 | +``` |
| 131 | + |
| 132 | +### 5. Access private content (requires signed cookies) |
| 133 | + |
| 134 | +```bash |
| 135 | +# Without cookies (should fail) |
| 136 | +curl https://{CLOUDFRONT_DOMAIN}/private/private_content.html |
| 137 | +``` |
| 138 | + |
| 139 | +Following commands will extract the signed cookies from the response and use them to access private content: |
| 140 | + |
| 141 | +```bash |
| 142 | +# Extract CloudFront signed cookie values from cookies.txt |
| 143 | +CF_POLICY=$(awk -F'\t' '$6=="CloudFront-Policy"{print $7; exit}' cookies.txt) |
| 144 | +CF_SIG=$(awk -F'\t' '$6=="CloudFront-Signature"{print $7; exit}' cookies.txt) |
| 145 | +CF_KID=$(awk -F'\t' '$6=="CloudFront-Key-Pair-Id"{print $7; exit}' cookies.txt) |
| 146 | + |
| 147 | +curl -H "Cookie: CloudFront-Policy=$CF_POLICY; CloudFront-Signature=$CF_SIG; CloudFront-Key-Pair-Id=$CF_KID" \ |
| 148 | + https://{CLOUDFRONT_DOMAIN}/private/private_content.html |
| 149 | +``` |
| 150 | + |
| 151 | +## Configuration |
| 152 | + |
| 153 | +The stack supports the following context variables in `cdk.json`: |
| 154 | + |
| 155 | +- `allowed_cors_origin`: CORS origin for API Gateway (default: "*") |
| 156 | +- `cookie_domain`: Domain for CloudFront signed cookies (optional) |
| 157 | +- `same_site`: SameSite attribute for cookies (default: "None") |
| 158 | +- `cookie_ttl_seconds`: TTL for signed cookies in seconds (default: 600) |
| 159 | + |
| 160 | +Example: |
| 161 | +```bash |
| 162 | +cdk deploy -c allowed_cors_origin="https://example.com" -c cookie_ttl_seconds=3600 |
| 163 | +``` |
| 164 | + |
| 165 | +## Cleanup |
| 166 | + |
| 167 | +1. Empty the S3 bucket (if you uploaded any content): |
| 168 | + ```bash |
| 169 | + aws s3 rm s3://{BUCKET_NAME} --recursive |
| 170 | + ``` |
| 171 | +2. Delete the stack: |
| 172 | + ```bash |
| 173 | + cdk destroy |
| 174 | + ``` |
| 175 | +3. Confirm the stack has been deleted: |
| 176 | + ```bash |
| 177 | + aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'CloudFrontSignedCookiesStack')].StackStatus" |
| 178 | + ``` |
| 179 | + |
| 180 | +---- |
| 181 | +Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 182 | +SPDX-License-Identifier: MIT-0 |
0 commit comments