Accessing S3 Bucket Objects Through CDN Using Cognito User Pool Auth and lambda edge

09 / Sep / 2025 by Shubham Rawat 0 comments

In this blog, I will walk you through the steps to secure access to S3 objects using Amazon CloudFront (CDN) integrated with Amazon Cognito authentication. The goal is to move away from easily shareable pre-signed URLs and instead enforce user in cognito user pool based, authenticated access. For example, a team may need to access sensitive PDFs but doesn’t want the risk of those links being forwarded to unauthorized users. By protecting the content behind a Cognito login page, only verified users of that team can access the files through the CDN link, ensuring that sensitive data stored in S3 is delivered securely and only to the right people.

Before we dive into the setup, let’s briefly go over the core AWS services involved:

  • CloudFront (CDN) with custom behaviors and S3 origins to cache and deliver content efficiently.
  • S3 Bucket as the secure storage for your files.
  • Cognito User Pool to handle user authentication and access control
  • Lambda edge

 

How the Flow Works ?

  • User requests a file → Example: https://mycdn.com/protected/report.pdf
  • CloudFront Behavior matches /protected/* path → This behavior is configured with:
    • Origin → S3 bucket (where your files live)
    • Lambda@Edge (Viewer Request trigger) → Runs before going to the origin to check authentication.
  • Lambda@Edge checks for Cognito session cookie →
    • If no cookie → Redirect user to Cognito login page.
    • If cookie present → Validate it.
  • User logs in via Cognito User Pool → Cognito verifies credentials.
  • Callback step: Once the user signs in, Cognito sends them over to the /callback endpoint along with an authorization code. A second Lambda@Edge runs here, exchanges the code (or simulates it in your code), sets a session cookie, and then redirects the user back to the /protected/* path.
  • Lambda@Edge validates the request again → Now sees the user is authenticated.
  • CloudFront forwards the request to S3 Origin → Fetches the actual file (report.pdf).
  • User gets the file securely → File is delivered via CloudFront only after passing Cognito login.

 

To make this use case follow the below steps

 

1. Create cloudfront distrbution.

CDN details

1.CDN

 

 

 

 

 

 

 

 

 

 

1.1. Create origin as s3 bucket where actual file which need to be delivered is placed.

Origin

1.1 CDN Origin

 

 

 

 

 

1.2. Create two behavior for CDN

Behavior

1.2 CDN Behavior

 

 

 

1.3. For the first behavior, set the path pattern to /callback*, point the origin to the S3 bucket where your files are stored, and attach an Origin Request Lambda@Edge function: arn:aws:lambda:us-east-1:051826723812:function:cognito-s3:34

callback

1.3 Callback Behavior

 

 

 

 

 

1.4. For the second behavior, configure the path pattern as /protected/*, use the same S3 bucket as the origin, and attach a Viewer Request Lambda@Edge function:arn:aws:lambda:us-east-1:051826723812:function:cognito-s3:33

1.4 Protected Behavior

1.4 Protected Behavior

 

 

 

 

2. Use AWS Cognito services to create user pool and app client

User Pool –

User Pool

2.User Pool

 

 

 

 

 

App Client-

Create User with email ID who want to access file we can create a common group email and whole team can use that to authenticate themselves while accessing the file . Make a note of Client ID , User pool ID and set Allowed callback URL (set it as <CDN URL/<behavior used for callback>>) these will be used in lambda edge code

2.1. app-client

2.1. app-client

 

 

2.3 Cognito-User-Creation

2.2 Cognito-User-Creation

 

 

 

 

 

 

 

 

 

 

 

3. Create Two lambda edge function used in CDN behavior above

First Lambda Code used in protected CDN behavior

import urllib.parse

# ===== CONFIG =====
COGNITO_REGION = “ap-south-1”
APP_CLIENT_ID = “2nlgjvvk6a6lj21n0t67fq1itj”
APP_CLIENT_SECRET = “lt4m91rbgqvovb5p1vkpbuje4vabibtikrslbd1ssngg8nut4s5”
COGNITO_DOMAIN = “https://ap-south-1edktvejzx.auth.ap-south-1.amazoncognito.com”
REDIRECT_URI = “https://d21wc134unren9.cloudfront.net/callback”
PROTECTED_URI =”/protected/S3_Cost_Optimization_Summary.xlsx_0.ods”
def lambda_handler(event, context):
request = event[‘Records’][0][‘cf’][‘request’]
headers = request.get(‘headers’, {})

print(“DEBUG: Incoming request URI:”, request[‘uri’])

# Check if request is for the protected path
if request[‘uri’].startswith(PROTECTED_URI):
print(“DEBUG: Protected path requested”)
# Check for session cookie
cookies = headers.get(‘cookie’, [])
has_session = any(‘CognitoAuthSession’ in cookie[‘value’] for cookie in cookies)

if not has_session:
# Redirect to Cognito login
login_url = (
f”{COGNITO_DOMAIN}/login?”
f”client_id={APP_CLIENT_ID}&”
f”redirect_uri={urllib.parse.quote(REDIRECT_URI)}&”
f”response_type=code&”
f”scope=openid+email+phone”
)
print(“DEBUG: No session found, redirecting to Cognito login:”, login_url)
return {
‘status’: ‘302’,
‘statusDescription’: ‘Found’,
‘headers’: {
‘location’: [{‘key’: ‘Location’, ‘value’: login_url}]
}
}
else:
print(“DEBUG: Session cookie found, allowing access”)
else:
print(“DEBUG: Non-protected URI, passing through”)

return request

Second Lambda Edge code used in callback CDN behavior

 

import urllib.parse
import base64

# ===== CONFIG =====
COGNITO_REGION = “ap-south-1”
APP_CLIENT_ID = “2nlgjvvk6a6lj21n0t67fq1itj”
APP_CLIENT_SECRET = “lt4m91rbgqvovb5p1vkpbuje4vabibtikrslbd1ssngg8nut4s5”
COGNITO_DOMAIN = “https://ap-south-1edktvejzx.auth.ap-south-1.amazoncognito.com”
REDIRECT_URI = “https://d21wc134unren9.cloudfront.net/callback”
=PROTECTED_URI =”/protected/S3_Cost_Optimization_Summary.xlsx_0.ods”

def lambda_handler(event, context):
request = event[‘Records’][0][‘cf’][‘request’]
querystring = request.get(‘querystring’, ”)

print(“DEBUG: Callback request received, querystring:”, querystring)

params = urllib.parse.parse_qs(querystring)
code = params.get(‘code’, [None])[0]

if not code:
print(“DEBUG: No auth code found in querystring, passing request through”)
return request

print(“DEBUG: Auth code received:”, code)

# Dummy session cookie (for testing)
session_cookie = base64.b64encode(code.encode()).decode()

print(“DEBUG: Setting session cookie and redirecting to protected URI:”, PROTECTED_URI)
return {
‘status’: ‘302’,
‘statusDescription’: ‘Found’,
‘headers’: {
‘location’: [{‘key’: ‘Location’, ‘value’: PROTECTED_URI}],
‘set-cookie’: [{
‘key’: ‘Set-Cookie’,
‘value’: f’CognitoAuthSession={session_cookie}; Path=/; Secure; HttpOnly’
}]
}
}

 

4. Create s3 bucket with below sample object path this should be align with cloudfront and lamdba edge code . In our use case we created S3 bucket s3://otp-frontend-shubham and inside it we created a path protected/  inside which there is actual file that we need to share to to end user.

S3-config

S3-config

 

 

 

 

 

 

S3-Object-Path

S3-Object-Path

 

 

 

 

 

5. Final Testing

Let’s assume we have a file placed in the above S3 path on a daily basis. An automation job runs at a fixed time, generates the file, and uploads it to the S3 bucket.

In a real-world scenario, the file name could include a timestamp or date (e.g., file_name09052025). In such cases, we can enhance the Lambda code to always pick the latest file and generate a CDN URL for it, which can then be shared with the end users via email.

For simplicity in this demo, I am using a static file named S3_Cost_Optimization_Summary.xlsx_0.ods, already placed in the S3 bucket, and sharing its CDN URL directly with the end user.

End users will try to access the file via this URL:

https://d21wc134unren9.cloudfront.net/protected/S3_Cost_Optimization_Summary.xlsx_0.ods

Since the user is not logged in, their first request to the URL will be intercepted and redirected to the Cognito login page for authentication.

cognito-login-page

cognito-login-page.Enter the user name and password for the user created in user pool of congnito services

 

 

 

 

 

 

 

cognito-login-username

cognito-login-username

cognito-password-page

cognito-password-page

 

 

 

 

 

file-downloaded

file-downloaded

 

 

 

 

 

 

 

 

Conclusion

In this setup we walked through how S3, CloudFront, Cognito, and Lambda@Edge can work together to lock down access to files. Instead of handing out public links, the content now sits safely behind a login page, and only the right users can fetch it. The best part is you still get the speed of a CDN along with strong access control. It’s a simple pattern you can reuse whenever you need to share files securely without exposing your bucket.

Thanks for reading till the end — I hope you walk away with something practical to apply

FOUND THIS USEFUL? SHARE IT

Tag -

aws cdn lambda s3

Leave a Reply

Your email address will not be published. Required fields are marked *