Search code examples
pythonamazon-web-servicesaws-lambdacorsaws-api-gateway

AWS Access to XMLHttpRequest at from origin has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header


I am trying to setup a view counter for the AWS #CloudResumeChallenge. I am following the instructions on these two youtube videos:

I am also using both of these gentlemen's GitHub repos as references:

I have followed the video's instructions, step-by-step; however, when I load my cloud resume at https://justinhenson.cloud/, the visitor counter has a endless spinning circle and I get the following error when I inspect the visitor counter element:

Access to XMLHttpRequest at 'https://pl8h7bxm3j.execute-api.us-east-1.amazonaws.com/' from origin 'https://justinhenson.cloud' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Failed to load resource: net::ERR_FAILED
pl8h7bxm3j.execute-api.us-east-1.amazonaws.com/:1

Below is my template.yaml file that I built with SAM that contains the configuration for my CloudFront Distribution under "MyDistribution":

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  cloud-resume

  Sample SAM Template for cloud-resume

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3

Resources:
  ResumeWebsite:
    Type: AWS::S3::Bucket
    Properties:
      AccessControl: PublicRead
      WebsiteConfiguration:
        IndexDocument: index.html 
      BucketName: justinhenson-cloud-resume-website

  MyRoute53Record:
    Type: "AWS::Route53::RecordSetGroup"
    Properties:
      HostedZoneId: Z01881203GO4SRLRJE2CO 
      RecordSets:
        - Name: justinhenson.cloud 
          Type: A
          AliasTarget:
            HostedZoneId: Z2FDTNDATAQYW2
            DNSName: !GetAtt MyDistribution.DomainName

  MyCertificate:
    Type: AWS::CertificateManager::Certificate
    Properties:
      DomainName: justinhenson.cloud 
      ValidationMethod: DNS

  MyDistribution:
    Type: "AWS::CloudFront::Distribution"
    Properties:
      DistributionConfig:
        ViewerCertificate:
            AcmCertificateArn: !Ref MyCertificate
            SslSupportMethod: sni-only
        Aliases: 
          - justinhenson.cloud
        DefaultCacheBehavior:
          ViewerProtocolPolicy: redirect-to-https
          TargetOriginId: justinhenson-cloud-resume-website.s3.us-east-1.amazonaws.com
          DefaultTTL: 0
          MinTTL: 0
          MaxTTL: 0
          ForwardedValues:
            QueryString: false
        Origins:
          - DomainName: justinhenson-cloud-resume-website.s3.us-east-1.amazonaws.com
            Id: justinhenson-cloud-resume-website.s3.us-east-1.amazonaws.com
            CustomOriginConfig:
              OriginProtocolPolicy: http-only
        Enabled: "true"
        DefaultRootObject: index.html

  BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties: 
      PolicyDocument:
        Id: MyPolicy
        Version: 2012-10-17
        Statement:
          - Sid: PublicReadForGetBucketObjects
            Effect: Allow
            Principal: "*"
            Action: "s3:GetObject"
            Resource: !Join
              - ""
              - - "arn:aws:s3:::"
                - !Ref ResumeWebsite
                - /*
      Bucket: !Ref ResumeWebsite

  MyDynamoDBTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: resume-website-app-tbl
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: "ID"
          AttributeType: "S"
      KeySchema:
        - AttributeName: "ID"
          KeyType: "HASH"

  MyLambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      Policies:
        - DynamoDBCrudPolicy:
            TableName: resume-website-app-tbl
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.9
      Architectures:
        - x86_64
      Events:
        Visits:
          Type: Api
          Properties:
            Path: /visit
            Method: post

  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.9
      Architectures:
        - x86_64
      Events:
        HelloWorld:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get

Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  HelloWorldApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn

Additionally, I followed both of the instructions in the videos by enabling the CORS policy in my lambda handler in the "app.py" file to be wide open to reduce complexity:

import boto3

dynamodb = boto3.resource('dynamodb', region_name='us-east-1')

table = dynamodb.Table('resume-website-app-tbl')

def lambda_handler(event, context):
    response = table.get_item(
        Key = {
            'ID':'visits'
        }
    )
    
    visit_count = response['Item']['counter'] 
    visit_count = str(int(visit_count) + 1)
    
    response = table.put_item(
        Item = {
            'ID':'visits',
            'counter': visit_count
        }
    )

    return {
        'statusCode': 200,
        'headers': {
            'Access-Control-Allow-Headers': '*',
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Methods': '*'
        },
        'body': json.dumps({'visit_count': visit_count})
    }

Also, as you can see in my scripts.js file below, I have pointed the pathway to the API Gateway to trigger the lambda handler:

"use strict";

$(document).ready(() => {
    $.post('https://pl8h7bxm3j.execute-api.us-east-1.amazonaws.com')
    .done(visitor_counter => {
        $('#loader').hide();
        $('#visits').text(visitor_counter);
    })
    .fail(e => {
        console.log('Error');
        console.log(e);
    });
});

I have tested my API Gateway with the Postman app and it returns the count without error:

enter image description here

And a copy of my index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
    <title>Justin Henson</title>
    <link rel="icon" type="image/x-icon" href="img/favicon.ico" />
    <meta name="robots" content="noindex,nofollow" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" />
    <link rel="stylesheet" href="css/styles.css" />
    <link rel="preconnect" href="https://fonts.gstatic.com" />
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700;900&display=swap" />
</head>
<body>
    <div class="container my-3 my-md-5">
        <div class="row">
            <header class="col-12 col-md-4 col-lg-4">
                <aside class="sidebar text-center px-2 px-md-3 px-lg-5 py-4 py-md-5">
                    <div class="sticky-wrap">
                        <div id="user-info" class="user-info">
                            <div class="about"><a href="img/profile.jpeg" alt="Jusitn Henson"><img src="img/profile.jpeg" alt="Jusitn Henson" class="profile-pic img-fluid rounded-circle" /></a>
                                <h1 class="name mt-3 mb-1">Justin Henson</h1>
                                <p class="job-name mb-0">AWSx2 / Cloud Engineer / Developer</p>
                                <p></p>
                                <p class="job-name mb-0"><a href="mailto:justin.henson@proton.me">Email Me / </a><a href="https://www.linkedin.com/in/justin-henson/">LinkedIn Profile</a></p>
                                <p class="job-name mb-0"> <a href="https://justinhenson.cloud/Justin-Henson-Resume.docx" target="_blank">Download My Resume</a></p>
                                <p class="job-name mb-0"><a href="tel:+15124877189">(512)-487-7189</a></p>
                                <p></p>
                                <div id="user-info" class="user-info">
                                    <div class="social-icons">
                                        <ul class="list-unstyled mb-0">
                                            <li>
                                                <a href="https://github.com/justin-scripts" class="test">
                                                    <svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                                        <title>GitHub icon</title>
                                                        <path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
                                                    </svg>
                                                </a>
                                            </li>
                                            <li>
                                                <a href="https://www.linkedin.com/in/justin-henson/">
                                                    <svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                                        <path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"></path>
                                                    </svg>
                                                </a>
                                            </li>
                                        </ul>
                                    </div>
                                    <div id="visitors" class="visitors"> You are visitor number<span id="visits"></span><span id="loader" class="loader"></span></div>
                            </div>
                        </div>
                    </div>
                </aside>
            </header>

            <main class="col-12 col-md-8 col-lg-8">
                <div class="content ml-0 ml-md-2 mr-0">
                    <section id="about" class="section mt-3 mt-md-0 p-3">
                        <h3 class="mb-3 text-uppercase">About</h3>
                        <p>
                            I enjoy mastering new technology and sharing that knowledge with others, while creating value for businesses and the cloud community.
                        </p>
                        <p>
                            This is a condensed resume for <a href="https://cloudresumechallenge.dev/">The Cloud Resume Challenge</a>. 
                            To view my full resume, please visit my <a href="https://www.linkedin.com/in/justin-henson/">LinkedIn Profile</a>.
                        </p>
                    </section>

                    <section id="experience" class="section mt-3 mt-md-5 p-3">
                        <h3 class="mb-3 text-uppercase">Experience</h3>
                        <div class="row mt-4 mb-2">
                            <div class="col-6"> 
                                <span class="tiny-super font-weight-bold">VarsityTutors.com</span> 
                                <span class="tiny-super d-block title">AWS / Python Tutor</span> 
                            </div>
                            <div class="col-6 text-right"> <span class="tiny-super date">Jan, 2022 - Current</span> </div>
                        </div>
                        <div class="row mt-4 mb-2">
                            <div class="col-6"> 
                                <span class="tiny-super font-weight-bold">Powur, PBC</span> 
                                <span class="tiny-super d-block title">Independent Solar Consultant</span> 
                            </div>
                            <div class="col-6 text-right"> <span class="tiny-super date">Mar, 2021 - Dec, 2021</span> </div>
                        </div>
                        <div class="row mt-4 mb-2">
                            <div class="col-6"> 
                                <span class="tiny-super font-weight-bold">Eco-Héroes De La Tierra</span> 
                                <span class="tiny-super d-block title">Founder & CEO</span> 
                            </div>
                            <div class="col-6 text-right"> <span class="tiny-super date">Jun, 2014 - Dec, 2021</span> </div>
                        </div>
                        <div class="row mt-4 mb-2">
                            <div class="col-6"> 
                                <span class="tiny-super font-weight-bold">The Amazon Rescue Center</span> 
                                <span class="tiny-super d-block title">Web Designer and Administrator</span> 
                            </div>
                            <div class="col-6 text-right"> <span class="tiny-super date">Feb, 2018 - Jun, 2019</span> </div>
                        </div>
                        <div class="row mt-4 mb-2">
                            <div class="col-6"> 
                                <span class="tiny-super font-weight-bold">Bottle Buildings & Bamboo Bicycles in Guatemala</span> 
                                <span class="tiny-super d-block title">Co-Founder & CEO</span> 
                            </div>
                            <div class="col-6 text-right"> <span class="tiny-super date">Nov, 2012 - Jun, 2014</span> </div>
                        </div>
                        <div class="row mt-4 mb-2">
                            <div class="col-6"> 
                                <span class="tiny-super font-weight-bold">Tainan City International English Village</span> 
                                <span class="tiny-super d-block title">English Second Language Teacher</span> 
                            </div>
                            <div class="col-6 text-right"> <span class="tiny-super date">Aug, 2010 - May, 2012</span> </div>
                        </div>
                        <div class="row mt-4 mb-2">
                            <div class="col-6"> 
                                <span class="tiny-super font-weight-bold">IBM</span> 
                                <span class="tiny-super d-block title">Network Software Engineer</span> 
                            </div>
                            <div class="col-6 text-right"> <span class="tiny-super date">Jun, 2007 - Jul, 2009</span> </div>
                        </div>
                        <div class="row mt-4 mb-2">
                            <div class="col-6"> 
                                <span class="tiny-super font-weight-bold">Windstead PC</span> 
                                <span class="tiny-super d-block title">IT Administrator</span> 
                            </div>
                            <div class="col-6 text-right"> <span class="tiny-super date">Aug, 2005 - Jun, 2007</span> </div>
                        </div>
                    </section>

                    <section id="education" class="section mt-3 mt-md-5 p-3">
                        <h3 class="mb-2 text-uppercase">Education</h3>
                        <div class="row mt-4 mb-2">
                            <div class="col-6"> 
                                <span class="tiny-super d-block font-weight-bold">Springboard</span> 
                                <span class="tiny-super">Program: 6 Month Data Science / ML Bootcamp</span> 
                            </div>
                            <div class="col-6 text-right"> <span class="tiny-super">2023 - To Present</span> </div>
                        </div>
                        <div class="row mt-4 mb-2">
                            <div class="col-6"> 
                                <span class="tiny-super d-block font-weight-bold">freecodecamp.org</span> 
                                <span class="tiny-super">Certificate: Responsive Web Design</span> 
                            </div>
                            <div class="col-6 text-right"> <span class="tiny-super">2022</span> </div>
                        </div>
                        <div class="row mt-4 mb-2">
                            <div class="col-6"> 
                                <span class="tiny-super d-block font-weight-bold">National Pingtung University of Science and Technology</span> 
                                <span class="tiny-super">Program: One Year Intensive Mandarin Study</span> 
                            </div>
                            <div class="col-6 text-right"> <span class="tiny-super">2012</span> </div>
                        </div>
                        <div class="row mt-4 mb-2">
                            <div class="col-6"> 
                                <span class="tiny-super d-block font-weight-bold">University of North Texas</span> 
                                <span class="tiny-super">Bachelor of Business Administration: Financial Analysis and Financial Management Services</span> 
                            </div>
                            <div class="col-6 text-right"> <span class="tiny-super">2001</span> </div>
                        </div>
                    </section>

                    <section id="certifications" class="section mt-3 mt-md-5 p-3">
                        <h3 class="mb-3 text-uppercase">Certifications</h3>
                        <div> 
                            <strong>AWS Certified Solutions Architect — Associate</strong>
                            <p>
                                <img src="img/aws_solutions_architect_assoc.png" class="aws-badge" alt="AWS Certified Solutions Architect Associate" />
                                <br />Issued November 09, 2022
                                <br /><a href="https://www.credly.com/badges/c73ccbf3-fc0e-4e08-a4ad-3f705553ae72/linked_in_profile">view credential</a>
                            </p>
                        </div>                        
                        <div>
                            <br /><br />
                            <strong>AWS Certified Cloud Practitioner</strong>
                            <p>
                                <img src="img/aws_cloud_practiotiner.png" class="aws-badge" alt="AWS Certified Cloud Practitioner" />
                                <br />Issued July 20, 2021
                                <br /><a href="https://www.credly.com/badges/c068a1c0-a90f-40ea-a6e0-d0e4cc5cac2b/linked_in_profile">view credential</a>
                              </p>
                        </div>
                        <div>
                            <br /><br />
                            <strong>CompTIA Network+ Certified</strong>
                            <p>
                                <img src="img/CompTIA_Network_Plus.png" class="aws-badge" alt="CompTIA Network+ Certified" />
                                <br />Issued February 27, 2006
                                <br /><a href="https://www.credly.com/badges/d5e353f4-672f-456c-ada6-ca0cedf7f2e9/public_url">view credential</a>
                              </p>
                        </div>
                    </section>

                    <section id="hobbies" class="section mt-3 mt-md-5 p-3">
                        <h3 class="mb-3 text-uppercase">Hobbies</h3>
                        <ul>
                            <li>Software Development</li>
                            <li>Learning new languages</li>
                            <li>Hiking</li>
                            <li>Travel</li>
                            <li>Cooking</li>
                        </ul>
                    </section>
                </div>
            </main>
        </div>
    </div>
    <script src="https://code.jquery.com/jquery-3.6.1.min.js" integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"></script>
    <script src="js/scripts.js"></script>
</body>
</html>

I have reviewed many other articules, but still unsure what the problem is. I would be most appreciative for any help or suggestions. Thanks in advance.


Solution

  • It looks like the API that you are calling in the script.js is not the same as the API you have put in the postman. API in script.js is https://pl8h7bxm3j.execute-api.us-east-1.amazonaws.com and that in postman is https://pl8h7bxm3j.execute-api.us-east-1.amazonaws.com/Prod/visit/. In the console log of your website the API in script.js is throwing a 403, which is in turn causing a CORS error as for 403 you have not enabled any CORS headers. enter image description here