Partial Outageshare-external
Video upload request blocked by CORS policy (localhost:3000)
1

I'm trying to upload a video from localhost:3000 using the Resumable Upload API, following this documentation.

I'm logging as a Facebook user using the Facebook Javascript SDK.

Then I fetch the list of Pages that user is managing.

Using the FB SDK (FB.api()), I make this request to create an upload session:

curl 'https://graph.facebook.com/v20.0/6923533587678832/uploads?access_token=<USER_ACCESS_TOKEN>' \
  -H 'accept: */*' \
  -H 'accept-language: en-GB,en-US;q=0.9,en;q=0.8' \
  -H 'content-type: application/x-www-form-urlencoded' \
  -H 'origin: https://localhost:3000' \
  -H 'priority: u=1, i' \
  -H 'referer: https://localhost:3000/' \
  -H 'sec-ch-ua: "Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "macOS"' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: cross-site' \
  -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' \
  --data-raw 'file_length=148397580&file_name=story_0.mp4&file_type=video%2Fmp4&method=post&pretty=0&sdk=joey&suppress_http_code=1'

Which returns a session id like { id: upload:xxxx}. So far, so good.

The next request, which initiates the upload, requires a binary file and headers, and I couldn't find a way to set headers on FB.api(), so I'm using axios instead.

export const startFbUpload = async (params: {
  uploadSessionId: string;
  accessToken: string;
  file: Blob;
}) => {
  const response = await axios.post<{ h: string }>(
    `https://graph.facebook.com/${FB_SDK_VERSION}/${params.uploadSessionId}`,
    params.file,
    {
      headers: {
        'Content-Type': params.file.type,
        Authorization: `OAuth ${params.accessToken}`,
        file_offset: '0',
        // 'Access-Control-Allow-Origin': '*',
      },
      maxBodyLength: Infinity,
      maxContentLength: Infinity,
      responseType: 'json',
    },
  );
  return response.data;
};

This method results in two requests, which I’ll display as curl requests. The first is the ‘preflight’ request:

curl 'https://graph.facebook.com/v20.0/upload:<SESSION_ID>' \
  -X 'POST' \
  -H 'sec-ch-ua: "Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'Authorization: OAuth <USER_ACCESS_TOKEN>' \
  -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' \
  -H 'Content-Type: video/mp4' \
  -H 'file_offset: 0' \
  -H 'Accept: application/json, text/plain, */*' \
  -H 'Referer: https://localhost:3000/' \
  -H 'sec-ch-ua-platform: "macOS"'

Which errors out with:

Access to XMLHttpRequest at 'https://graph.facebook.com/v20.0/upload:<SESSION_ID> from origin 'https://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Then there’s also this this OPTIONS request

curl 'https://graph.facebook.com/v20.0/upload:<SESSION_ID>?sig=ARaX46WhGl9x_QuFUpE' \
  -X 'OPTIONS' \
  -H 'accept: */*' \
  -H 'accept-language: en-GB,en-US;q=0.9,en;q=0.8' \
  -H 'access-control-request-headers: authorization,content-type,file_offset' \
  -H 'access-control-request-method: POST' \
  -H 'origin: https://localhost:3000' \
  -H 'priority: u=1, i' \
  -H 'referer: https://localhost:3000/' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: cross-site' \
  -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'

Which returns a 403 error.

Being a CORS error, I’m thinking the solution lies in the Facebook app configuration. I’ve set “Allowed Domains for the JavaScript SDK” to include localhost:3000, but I don’t see any config options related to CORS.

I believe I have the correct permissions configured. The FB app’s type is ‘business’, but it doesn’t have ‘advanced access’ yet (still waiting for business and access verification). However, I don’t think that should matter because the Facebook user I’m making the requests with is directly associated with the Facebook app.

What do I need to do to get past this CORS / 403 error?

Mark
Asked about 5 months ago
Selected Answer
1

I couldn't find a way to configure Facebook to accept video post from localhost (or any other host, for that matter).

But I did learn that this problem is usually solved by using a "CORS proxy". Rather than making the request direct to Facebook from the browser, send the request to the CORS proxy, and the proxy redirects it on to Facebook (or whatever other third party / cross-domain). Because the proxy is a server and not a browser, CORS is not an issue.

There are many ways to set up a CORS proxy. In my case, it's a Next.js app, so I just used Next's rewrites() functionality as the CORS proxy.

const nextConfig = {
  async rewrites() {
    return [
      {
        source: '/facebookgraph/:sdk/:uploadId',
        destination: 'https://graph.facebook.com/:sdk/:uploadId',
      },
    ]
  },
};
July 8 at 4:05 PM
Mark
Selected Answer
2

You cannot upload from localhost

July 1 at 1:50 AM
Lars
Mark

Thanks, that's good to know.

How do you know that? Is that documented anywhere by Facebook?

July 1 at 6:08 PM
Mark

And how can we develop locally if we can't post from localhost? I tried ngrok, but the Facebook SDKwon't recognise it as a host domain (yes, I entered it as a host domain in the App Dashboard).

July 1 at 6:34 PM