{"id":75632,"date":"2025-09-09T11:21:59","date_gmt":"2025-09-09T05:51:59","guid":{"rendered":"https:\/\/www.tothenew.com\/blog\/?p=75632"},"modified":"2025-09-09T14:49:22","modified_gmt":"2025-09-09T09:19:22","slug":"accessing-s3-bucket-objects-through-cdn-using-cognito-user-pool-auth-and-lambda-edge","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/accessing-s3-bucket-objects-through-cdn-using-cognito-user-pool-auth-and-lambda-edge\/","title":{"rendered":"Accessing S3 Bucket Objects Through CDN Using Cognito User Pool Auth and lambda edge"},"content":{"rendered":"<p>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\u2019t 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.<\/p>\n<p>Before we dive into the setup, let\u2019s briefly go over the core AWS services involved:<\/p>\n<ul style=\"list-style-type: square;\">\n<li>CloudFront (CDN) with custom behaviors and S3 origins to cache and deliver content efficiently.<\/li>\n<li>S3 Bucket as the secure storage for your files.<\/li>\n<li>Cognito User Pool to handle user authentication and access control<\/li>\n<li>Lambda edge<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>How the Flow Works ?<\/p>\n<ul style=\"list-style-type: square;\">\n<li>User requests a file \u2192 Example: https:\/\/mycdn.com\/protected\/report.pdf<\/li>\n<li>CloudFront Behavior matches \/protected\/* path \u2192 This behavior is configured with:\n<ul style=\"list-style-type: circle;\">\n<li>Origin \u2192 S3 bucket (where your files live)<\/li>\n<li>Lambda@Edge (Viewer Request trigger) \u2192 Runs before going to the origin to check authentication.<\/li>\n<\/ul>\n<\/li>\n<li>Lambda@Edge checks for Cognito session cookie \u2192\n<ul style=\"list-style-type: circle;\">\n<li>If no cookie \u2192 Redirect user to Cognito login page.<\/li>\n<li>If cookie present \u2192 Validate it.<\/li>\n<\/ul>\n<\/li>\n<li>User logs in via Cognito User Pool \u2192 Cognito verifies credentials.<\/li>\n<li>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.<\/li>\n<li>Lambda@Edge validates the request again \u2192 Now sees the user is authenticated.<\/li>\n<li>CloudFront forwards the request to S3 Origin \u2192 Fetches the actual file (report.pdf).<\/li>\n<li>User gets the file securely \u2192 File is delivered via CloudFront only after passing Cognito login.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>To make this use case follow the below steps<\/p>\n<p>&nbsp;<\/p>\n<h1>1. Create cloudfront distrbution.<\/h1>\n<div id=\"attachment_75635\" style=\"width: 842px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-75635\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-75635 \" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-13-54-18-1024x435.png\" alt=\"CDN details\" width=\"832\" height=\"353\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-13-54-18-1024x435.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-13-54-18-300x127.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-13-54-18-768x326.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-13-54-18-1536x653.png 1536w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-13-54-18-624x265.png 624w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-13-54-18.png 1570w\" sizes=\"(max-width: 832px) 100vw, 832px\" \/><p id=\"caption-attachment-75635\" class=\"wp-caption-text\">1.CDN<\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h1>1.1. Create origin as s3 bucket where actual file which need to be delivered is placed.<\/h1>\n<div id=\"attachment_75638\" style=\"width: 781px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-75638\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-75638 \" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-13-58-18.png\" alt=\"Origin\" width=\"771\" height=\"149\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-13-58-18.png 1533w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-13-58-18-300x58.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-13-58-18-1024x198.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-13-58-18-768x148.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-13-58-18-624x120.png 624w\" sizes=\"(max-width: 771px) 100vw, 771px\" \/><p id=\"caption-attachment-75638\" class=\"wp-caption-text\">1.1 CDN Origin<\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h1>1.2. Create two behavior for CDN<\/h1>\n<div id=\"attachment_75640\" style=\"width: 776px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-75640\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-75640 \" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-01-18-1024x52.png\" alt=\"Behavior\" width=\"766\" height=\"38\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-01-18-1024x52.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-01-18-300x15.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-01-18-768x39.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-01-18-624x32.png 624w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-01-18.png 1509w\" sizes=\"(max-width: 766px) 100vw, 766px\" \/><p id=\"caption-attachment-75640\" class=\"wp-caption-text\">1.2 CDN Behavior<\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h1>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<\/h1>\n<div id=\"attachment_75644\" style=\"width: 851px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-75644\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-75644 \" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-11-56-1024x181.png\" alt=\"callback\" width=\"841\" height=\"148\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-11-56-1024x181.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-11-56-300x53.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-11-56-768x135.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-11-56-1536x271.png 1536w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-11-56-624x110.png 624w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-11-56.png 1661w\" sizes=\"(max-width: 841px) 100vw, 841px\" \/><p id=\"caption-attachment-75644\" class=\"wp-caption-text\">1.3 Callback Behavior<\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h1>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<\/h1>\n<div id=\"attachment_75647\" style=\"width: 902px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-75647\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-75647 \" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-15-38-1024x181.png\" alt=\"1.4 Protected Behavior\" width=\"892\" height=\"157\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-15-38-1024x181.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-15-38-300x53.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-15-38-768x135.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-15-38-1536x271.png 1536w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-15-38-624x110.png 624w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-15-38.png 1661w\" sizes=\"(max-width: 892px) 100vw, 892px\" \/><p id=\"caption-attachment-75647\" class=\"wp-caption-text\">1.4 Protected Behavior<\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h1>2. Use AWS Cognito services to create user pool and app client<\/h1>\n<p>User Pool &#8211;<\/p>\n<div id=\"attachment_75652\" style=\"width: 854px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-75652\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-75652 \" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-32-00-1024x215.png\" alt=\"User Pool\" width=\"844\" height=\"177\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-32-00-1024x215.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-32-00-300x63.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-32-00-768x161.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-32-00-1536x323.png 1536w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-32-00-624x131.png 624w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-32-00.png 1557w\" sizes=\"(max-width: 844px) 100vw, 844px\" \/><p id=\"caption-attachment-75652\" class=\"wp-caption-text\">2.User Pool<\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>App Client-<\/p>\n<p>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 &lt;CDN URL\/&lt;behavior used for callback&gt;&gt;) these will be used in lambda edge code<\/p>\n<div id=\"attachment_75653\" style=\"width: 635px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-75653\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-75653 size-large\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-35-14-1024x531.png\" alt=\"2.1. app-client\" width=\"625\" height=\"324\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-35-14-1024x531.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-35-14-300x156.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-35-14-768x399.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-35-14-624x324.png 624w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-14-35-14.png 1528w\" sizes=\"(max-width: 625px) 100vw, 625px\" \/><p id=\"caption-attachment-75653\" class=\"wp-caption-text\">2.1. app-client<\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<div id=\"attachment_75733\" style=\"width: 808px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-75733\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-75733 \" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-33-29-1024x166.png\" alt=\"2.3 Cognito-User-Creation\" width=\"798\" height=\"129\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-33-29-1024x166.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-33-29-300x49.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-33-29-768x125.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-33-29-624x101.png 624w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-33-29.png 1505w\" sizes=\"(max-width: 798px) 100vw, 798px\" \/><p id=\"caption-attachment-75733\" class=\"wp-caption-text\">2.2 Cognito-User-Creation<\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h1>3. Create Two lambda edge function used in CDN behavior above<\/h1>\n<p><strong>First Lambda Code used in protected CDN behavior<\/strong><\/p>\n<blockquote><p>import urllib.parse<\/p>\n<p># ===== CONFIG =====<br \/>\nCOGNITO_REGION = &#8220;ap-south-1&#8221;<br \/>\nAPP_CLIENT_ID = &#8220;2nlgjvvk6a6lj21n0t67fq1itj&#8221;<br \/>\nAPP_CLIENT_SECRET = &#8220;lt4m91rbgqvovb5p1vkpbuje4vabibtikrslbd1ssngg8nut4s5&#8221;<br \/>\nCOGNITO_DOMAIN = &#8220;https:\/\/ap-south-1edktvejzx.auth.ap-south-1.amazoncognito.com&#8221;<br \/>\nREDIRECT_URI = &#8220;https:\/\/d21wc134unren9.cloudfront.net\/callback&#8221;<br \/>\nPROTECTED_URI =&#8221;\/protected\/S3_Cost_Optimization_Summary.xlsx_0.ods&#8221;<br \/>\ndef lambda_handler(event, context):<br \/>\nrequest = event[&#8216;Records&#8217;][0][&#8216;cf&#8217;][&#8216;request&#8217;]<br \/>\nheaders = request.get(&#8216;headers&#8217;, {})<\/p>\n<p>print(&#8220;DEBUG: Incoming request URI:&#8221;, request[&#8216;uri&#8217;])<\/p>\n<p># Check if request is for the protected path<br \/>\nif request[&#8216;uri&#8217;].startswith(PROTECTED_URI):<br \/>\nprint(&#8220;DEBUG: Protected path requested&#8221;)<br \/>\n# Check for session cookie<br \/>\ncookies = headers.get(&#8216;cookie&#8217;, [])<br \/>\nhas_session = any(&#8216;CognitoAuthSession&#8217; in cookie[&#8216;value&#8217;] for cookie in cookies)<\/p>\n<p>if not has_session:<br \/>\n# Redirect to Cognito login<br \/>\nlogin_url = (<br \/>\nf&#8221;{COGNITO_DOMAIN}\/login?&#8221;<br \/>\nf&#8221;client_id={APP_CLIENT_ID}&amp;&#8221;<br \/>\nf&#8221;redirect_uri={urllib.parse.quote(REDIRECT_URI)}&amp;&#8221;<br \/>\nf&#8221;response_type=code&amp;&#8221;<br \/>\nf&#8221;scope=openid+email+phone&#8221;<br \/>\n)<br \/>\nprint(&#8220;DEBUG: No session found, redirecting to Cognito login:&#8221;, login_url)<br \/>\nreturn {<br \/>\n&#8216;status&#8217;: &#8216;302&#8217;,<br \/>\n&#8216;statusDescription&#8217;: &#8216;Found&#8217;,<br \/>\n&#8216;headers&#8217;: {<br \/>\n&#8216;location&#8217;: [{&#8216;key&#8217;: &#8216;Location&#8217;, &#8216;value&#8217;: login_url}]<br \/>\n}<br \/>\n}<br \/>\nelse:<br \/>\nprint(&#8220;DEBUG: Session cookie found, allowing access&#8221;)<br \/>\nelse:<br \/>\nprint(&#8220;DEBUG: Non-protected URI, passing through&#8221;)<\/p>\n<p>return request<\/p><\/blockquote>\n<p><strong><!--more-->Second Lambda Edge code used in callback CDN behavior<\/strong><\/p>\n<p>&nbsp;<\/p>\n<blockquote><p>import urllib.parse<br \/>\nimport base64<\/p>\n<p># ===== CONFIG =====<br \/>\nCOGNITO_REGION = &#8220;ap-south-1&#8221;<br \/>\nAPP_CLIENT_ID = &#8220;2nlgjvvk6a6lj21n0t67fq1itj&#8221;<br \/>\nAPP_CLIENT_SECRET = &#8220;lt4m91rbgqvovb5p1vkpbuje4vabibtikrslbd1ssngg8nut4s5&#8221;<br \/>\nCOGNITO_DOMAIN = &#8220;https:\/\/ap-south-1edktvejzx.auth.ap-south-1.amazoncognito.com&#8221;<br \/>\nREDIRECT_URI = &#8220;https:\/\/d21wc134unren9.cloudfront.net\/callback&#8221;<br \/>\n=PROTECTED_URI =&#8221;\/protected\/S3_Cost_Optimization_Summary.xlsx_0.ods&#8221;<\/p>\n<p>def lambda_handler(event, context):<br \/>\nrequest = event[&#8216;Records&#8217;][0][&#8216;cf&#8217;][&#8216;request&#8217;]<br \/>\nquerystring = request.get(&#8216;querystring&#8217;, &#8221;)<\/p>\n<p>print(&#8220;DEBUG: Callback request received, querystring:&#8221;, querystring)<\/p>\n<p>params = urllib.parse.parse_qs(querystring)<br \/>\ncode = params.get(&#8216;code&#8217;, [None])[0]<\/p>\n<p>if not code:<br \/>\nprint(&#8220;DEBUG: No auth code found in querystring, passing request through&#8221;)<br \/>\nreturn request<\/p>\n<p>print(&#8220;DEBUG: Auth code received:&#8221;, code)<\/p>\n<p># Dummy session cookie (for testing)<br \/>\nsession_cookie = base64.b64encode(code.encode()).decode()<\/p>\n<p>print(&#8220;DEBUG: Setting session cookie and redirecting to protected URI:&#8221;, PROTECTED_URI)<br \/>\nreturn {<br \/>\n&#8216;status&#8217;: &#8216;302&#8217;,<br \/>\n&#8216;statusDescription&#8217;: &#8216;Found&#8217;,<br \/>\n&#8216;headers&#8217;: {<br \/>\n&#8216;location&#8217;: [{&#8216;key&#8217;: &#8216;Location&#8217;, &#8216;value&#8217;: PROTECTED_URI}],<br \/>\n&#8216;set-cookie&#8217;: [{<br \/>\n&#8216;key&#8217;: &#8216;Set-Cookie&#8217;,<br \/>\n&#8216;value&#8217;: f&#8217;CognitoAuthSession={session_cookie}; Path=\/; Secure; HttpOnly&#8217;<br \/>\n}]<br \/>\n}<br \/>\n}<\/p>\n<p>&nbsp;<\/p><\/blockquote>\n<h1>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 <span style=\"color: #99cc00;\"><strong><em>s3:\/\/otp-frontend-shubham<\/em> <\/strong><\/span>and inside it we created a path<span style=\"color: #99cc00;\"><strong> protected\/\u00a0 <\/strong><\/span>inside which there is actual file that we need to share to to end user.<\/h1>\n<div id=\"attachment_75722\" style=\"width: 635px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-75722\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-75722 size-large\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-18-35-25-1024x316.png\" alt=\"S3-config\" width=\"625\" height=\"193\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-18-35-25-1024x316.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-18-35-25-300x93.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-18-35-25-768x237.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-18-35-25-624x193.png 624w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-18-35-25.png 1521w\" sizes=\"(max-width: 625px) 100vw, 625px\" \/><p id=\"caption-attachment-75722\" class=\"wp-caption-text\">S3-config<\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<div id=\"attachment_75723\" style=\"width: 635px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-75723\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-75723 size-large\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-18-36-09-1024x316.png\" alt=\"S3-Object-Path\" width=\"625\" height=\"193\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-18-36-09-1024x316.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-18-36-09-300x93.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-18-36-09-768x237.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-18-36-09-624x193.png 624w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-18-36-09.png 1521w\" sizes=\"(max-width: 625px) 100vw, 625px\" \/><p id=\"caption-attachment-75723\" class=\"wp-caption-text\">S3-Object-Path<\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h1>5. <strong>Final Testing<\/strong><\/h1>\n<p>Let\u2019s 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.<\/p>\n<p>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.<\/p>\n<p>For simplicity in this demo, I am using a static file named <strong>S3_Cost_Optimization_Summary.xlsx_0.ods<\/strong>, already placed in the S3 bucket, and sharing its CDN URL directly with the end user.<\/p>\n<p>End users will try to access the file via this URL:<\/p>\n<p><em><span style=\"color: #99cc00;\">https:\/\/d21wc134unren9.cloudfront.net\/protected\/S3_Cost_Optimization_Summary.xlsx_0.ods<\/span><\/em><\/p>\n<p>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.<\/p>\n<div id=\"attachment_75734\" style=\"width: 635px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-75734\" decoding=\"async\" loading=\"lazy\" class=\"size-large wp-image-75734\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-48-32-1024x536.png\" alt=\"cognito-login-page\" width=\"625\" height=\"327\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-48-32-1024x536.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-48-32-300x157.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-48-32-768x402.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-48-32-1536x804.png 1536w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-48-32-624x327.png 624w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-48-32.png 1818w\" sizes=\"(max-width: 625px) 100vw, 625px\" \/><p id=\"caption-attachment-75734\" class=\"wp-caption-text\">cognito-login-page.Enter the user name and password for the user created in user pool of congnito services<\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<div id=\"attachment_75735\" style=\"width: 694px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-75735\" decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-75735\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-50-15.png\" alt=\"cognito-login-username\" width=\"684\" height=\"410\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-50-15.png 684w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-50-15-300x180.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-50-15-624x374.png 624w\" sizes=\"(max-width: 684px) 100vw, 684px\" \/><p id=\"caption-attachment-75735\" class=\"wp-caption-text\">cognito-login-username<\/p><\/div>\n<div id=\"attachment_75736\" style=\"width: 804px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-75736\" decoding=\"async\" loading=\"lazy\" class=\" wp-image-75736\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-52-30-1024x641.png\" alt=\"cognito-password-page\" width=\"794\" height=\"497\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-52-30-1024x641.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-52-30-300x188.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-52-30-768x481.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-52-30-624x390.png 624w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-52-30.png 1157w\" sizes=\"(max-width: 794px) 100vw, 794px\" \/><p id=\"caption-attachment-75736\" class=\"wp-caption-text\">cognito-password-page<\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<div id=\"attachment_75737\" style=\"width: 870px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-75737\" decoding=\"async\" loading=\"lazy\" class=\" wp-image-75737\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-54-41-1024x182.png\" alt=\"file-downloaded\" width=\"860\" height=\"152\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-54-41-1024x182.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-54-41-300x53.png 300w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-54-41-768x136.png 768w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-54-41-1536x273.png 1536w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-54-41-624x111.png 624w, \/blog\/wp-ttn-blog\/uploads\/2025\/09\/Screenshot-from-2025-09-05-19-54-41.png 1610w\" sizes=\"(max-width: 860px) 100vw, 860px\" \/><p id=\"caption-attachment-75737\" class=\"wp-caption-text\">file-downloaded<\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h1><strong>Conclusion<\/strong><\/h1>\n<p>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\u2019s a simple pattern you can reuse whenever you need to share files securely without exposing your bucket.<\/p>\n<p><strong><em>Thanks for reading till the end \u2014 I hope you walk away with something practical to apply<\/em><\/strong><\/p>\n<p><code><\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 [&hellip;]<\/p>\n","protected":false},"author":2172,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":120},"categories":[5877],"tags":[248,2645,1545,670],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/75632"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/2172"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=75632"}],"version-history":[{"count":34,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/75632\/revisions"}],"predecessor-version":[{"id":76129,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/75632\/revisions\/76129"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=75632"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=75632"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=75632"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}