Fetch does not send body in POST request (request.get_json(silent=True) is empty)

Question:

I am having the following custom component in which I am trying to send a HTTP post request to a server. The curl of this command is as follows:

curl --location --request POST 'https://CLOUD-FUNCTION-HTTP-URL' 
--header 'Content-Type: application/json' 
--header 'Authorization: Bearer foo' 
--header 'Accept: application/json' 
--data-raw '{
    "message": "hello"
}'

When I send the request with Postman, my server does receive the content of the body.

However, when I send it from my React app, the body is not found (the content is empty).

On the back-end side, I combined the following two tutorials

What is causing this mismatch?

import * as React from "react";
import { useState } from "react";
import CircularProgress from "@mui/material/CircularProgress";
import { Button, TextField } from "@mui/material";
import { getBearerToken } from "../auth/firebase/AuthProvider";

export default function Wrapper() {
  const [prompt, setPrompt] = useState("Do something");
  const [data, setData] = useState([]);
  const [status, setStatus] = useState(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    setStatus("loading");

    getBearerToken().then(
      function (token) {success(token);},
      function (error) {setStatus("error");}
    );

    function success(token) {
      const requestOptions = {
        method: "POST",
        headers: {
          "Accept": "application/json",
          "Content-Type": "application/json",
          "Authorization": "Bearer " + token
        },
        body: JSON.stringify({
          "message": "hello"
        }),
      }

      fetch("https://CLOUD-FUNCTION-HTTP-URL", requestOptions)
        .then((res) => res.json())
        .then((data) => {
          console.log(data);
          setData(data);
          setStatus("success");
        })
        .catch((err) => {
          console.log(err)
          console.log(err.message);
          setStatus("error");
        });
    }
  };

  function handleSetPrompt(e) {
    setPrompt(e.target.value);
  }

  return (
    <>
      <form onSubmit={handleSubmit}>
        <TextField
          id="standard-basic"
          label="Standard"
          variant="standard"
          onChange={handleSetPrompt}
          value={prompt}
        />
        <Button color="inherit" type="submit">Fetch</Button>
        <LoadingSpinner shouldHide={status !== "loading"} />
      </form>
      <div>
        <p>{prompt}</p>
        <p>{status}</p>
      </div>
    </>
  );
}

const LoadingSpinner = (props) => (
  <div className={props.shouldHide ? "hidden" : undefined}>
    <CircularProgress></CircularProgress>
  </div>
);
Asked By: JohnAndrews

||

Answers:

The solution to the problem was that I did not had to include the Bearer part, as the method on the back-end that was used to validate did not require a Bearer token but rather an identity token (see link below).

Back-end/Python:

from firebase_admin import auth, initialize_app, get_app
id_token = headers.get("Authorization")

# Verify the ID token while checking if the token is revoked by passing check_revoked=True
decoded_token = auth.verify_id_token(id_token, check_revoked=True) 

Front-end/React/JS

export function getIdToken() {
  return auth.currentUser.getIdToken(true).then(
    function(token) {
      return token;
    },
    function(error) {
      console.log(error)
      return error;
    }
  )
}

Related resources:

Answered By: JohnAndrews