Skip to content

Commit 77ec8a4

Browse files
authored
Merge pull request #2913 from mate329/mate329-feature-cloudfront-s3-signed-cookies-cognito
New serverless pattern - cloudfront-s3-signed-cookies-cognito
2 parents cedbd5a + 9ab5c0c commit 77ec8a4

10 files changed

Lines changed: 1354 additions & 0 deletions

File tree

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
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

Comments
 (0)