CloudFront link used in React app's API calls instead of correct API URL

0

Hi all,

The Gist

My React web app's API requests are failing with status 403 (forbidden) due to the AWS CloudFront distribution's domain name being used as the API's base URL. Opening the Network tab in the browser's dev console and then attempting to log into my app, I see that the request URL is the following:

https://<my_cf_distribution_link>/<my_api_url>/login

When it should of course be:

https://<my_api_url>/login

Some additional info

My AWS pipeline is setup with CodeBuild, S3 & CloudFront. When I enable static hosting on my S3 bucket and attempt to login from the bucket's domain then everything works as expected. I have tried changing various settings on my CF distribution like the viewer protocol behaviour, but nothing worked. As far as I know, I'm not using API Gateway or Lambda. Also, just to clarify, my S3 bucket is publicly accessible with a Bucket Policy in place and the CF's origin access setting is set to public as well. The site content itself is served perfectly fine with the CF domain link, it's literally just the API requests that are failing.

I'm using axios in my React app to handle the API requests. I have attached some screenshots of the code responsible for sending the request, the browser console output of the runtime values for the URL & configuration, as well as the API request headers. The sensitive information has been removed for protection.

Thanks in advance for any assistance with the above!

code console headers

Willie
已提問 4 個月前檢視次數 307 次
2 個答案
1
已接受的答案

Not sure whether this will help anyone that has a similar issue because the solution to my specific situation lies in the implementation of the axios library to request API calls, and is not related to any AWS configuration in particular.

The Culprit

In my React app, I have a class "BaseController" that handles all my API calls. In the class's constructor I read the backend's base API URL from the ".env" file and stores it in a constant field. When the API request is made, I take the endpoint of the API (e.g. "/login") and append it to the base URL and then pass the result through to axios.

import axios from "axios";

const BASE_URL = process.env.REACT_APP_BASE_URL;

export default class BaseController {
    constructor() {
        this.BASE_URL = BASE_URL;
    }

    sendRequest = async (method, url, body) => {

        let config = {
            method: method
        };
        
	url = `${this.BASE_URL}/${url}`;
		
        // Remove any duplicate '/'
        url = url.replace(/\/+/g, "/");

        if (params) {
            config.params = this.addQueryParameters(url, params);
        }

        if (body !== null) {
            config.data = JSON.stringify(body);
        }

        const response = await axios(url, config);

        return response.data;
    };
}

However, the URL passed through to axios is not read as the absolute URL that it should use. So, when the request is made from the CloudFront domain, axios uses the CF domain as its base URL. Why this is not happening on my local machine during development, or even in a statically hosted S3 bucket domain, I have no idea.

The solution

Properly set the default base URL for axios, like so:

axios.defaults.baseURL = baseURL;

So the above function would then look like this:

import axios from "axios";

const BASE_URL = process.env.REACT_APP_BASE_URL;

export default class BaseController {
    constructor() {
		axios.defaults.baseURL = BASE_URL;
    }

    sendRequest = async (method, url, body) => {

        let config = {
            method: method
        };
		
        // Remove any duplicate '/'
        url = url.replace(/\/+/g, "/");

        if (params) {
            config.params = this.addQueryParameters(url, params);
        }

        if (body !== null) {
            config.data = JSON.stringify(body);
        }

        const response = await axios(url, config);

        return response.data;
    };
}

And then it would be used like so (very simplified usage example):

import BaseController from "./BaseController";

const response = await BaseController.sendRequest("POST", "/login", credentials);

Hope this helps someone!

Willie
已回答 4 個月前
profile picture
專家
已審閱 2 個月前
0

Your endpoint appears to run successfully, but you cannot make POST login requests. You most likely forgot to enable the POST method.

Enter image description here

https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CacheBehavior.html#:~:text=Required%3A%20Yes-,AllowedMethods,-A%20complex%20type

profile picture
已回答 4 個月前
  • Thanks for your response! I updated the CF distribution's behavior setting to allow all the HTTP methods, but the issue persists

您尚未登入。 登入 去張貼答案。

一個好的回答可以清楚地回答問題並提供建設性的意見回饋,同時有助於提問者的專業成長。

回答問題指南