0

I have familiarized myself with CORS and have sorted through previous problems in the past during the development of this project.

Here's the problem. I have a full-stack Node.js web app that I am building. The impl is 85% to 90%; hence, I am in the process of porting the code over to the cloud for showcasing.

My deployment approach is as follows:

  • endpoint A - plain static website (mikesmith.me)
  • endpoint B - FE of fullstack web app (appdemo.mikesmith.me)
  • endpoint C - BE of the fullstack web app (api.mikesmith.me)

Before proceeding, there are a few points worthy of recognition:

  • I am deploying these apps as services managed by a combination of app.yaml files and a single dispatch.yaml file.
  • As such, the URLs generated by the GAE deploy command follow a template and don't match what I've shown above. I have, however, ensured that URL business is taken care of accordingly, such as the CORS configuration server-side.

A CORS error

When the deployment finishes and I browse the client, I check the console and am greeted with the error that can be seen in the picture above.

The strange this is, I have configured CORS in api/app.js:

var app = express();

// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "jade");

app.use(cors({ origin: ["https://shoedawg-client-dot-website-255218.uc.r.appspot.com"], credentials: true }));
app.use(function (req, res, next) {
    res.setHeader("Access-Control-Allow-Origin", "https://shoedawg-client-dot-website-255218.uc.r.appspot.com");
    res.setHeader("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PUT, PATCH, DELETE");
    res.setHeader("Access-Control-Allow-Headers", "X-Requested-With, content-type, Authorization");
    next();

Again, see the URL of the image the see that the URL supplied to the Access-Control-Allow-Origin header is a match. I even try to set origin in the same breath to try and be absolutely sure that the CORS option is enabled. All that in place and it still blocks.

I have thought about messing around with modifying headers in my API's app.yaml file, but my feeling is that I shouldn't need to do this if I can take care of CORS at the application level.

What am I doing wrong here? Including dispatch.yaml and app.yaml files to provide as much info as possible.

shoedawg-client.yaml:

service: shoedawg-client
runtime: nodejs18
instance_class: F2

handlers:
    - url: "/"
      script: public/index.html

shoedawg-server.yaml:

service: shoedawg-server
runtime: nodejs18
instance_class: F2

dispatch.yaml:

dispatch:
    - url: "shoedawg-server-dot-website-255218.uc.r.appspot.com/*"
      service: shoedawg-server

    - url: "shoedawg-client-dot-website-255218.uc.r.appspot.com/*"
      service: shoedawg-client

    - url: "*/.*"
      service: default

What do we think? Let me know if you need more info. Also feel free to critique my .yaml; I am just getting back into GCP and would love to learn some best practices.

For those wondering, here is the code that makes the actual request:

import axios from "../api/axios";

export const validateToken = async (triggerLoginFn, isLoggedIn) => {
    try {
        await axios
            .get("/refreshToken", {
                headers: { "Content-Type": "application/json" },
                withCredentials: true,
            })
            .then((res) => {
                if (res.data.renewedAccessToken !== undefined) {
                    triggerLoginFn({
                        email: res.data.email,
                        fname: res.data.fname,
                        accessToken: res.data.renewedAccessToken,
                    });
                } else {
                    console.error("(validateRefreshToken): Login Fn Failed!");
                }
            });
    } catch (error) {
        if (!isLoggedIn) {
            // we only care to see this if we haven't already validated.
            console.error(error.response.data);
        }
    }
};

validateToken() is then imported into LandingPage.js and called from there:

const LandingPage = (props) => {
    const isLoggedIn = props.isLoggedIn;
    const triggerLogin = props.triggerLogin;
    const triggerLogout = props.triggerLogout;
    const navigate = useNavigate();

    useEffect(() => {
        if (props.accessToken !== undefined) {
            // If we have an access token, this means our refresh token is valid.
            // Hence, no need to validate.
            return;
        }
        console.log("initializing LandingPage..");
        const initValidation = async () => {
            await validateToken(triggerLogin, isLoggedIn);
        };

        initValidation();
    });

EDIT #1: Removed trailing slashes from client URLs; still getting blocked by CORS. EDIT #2: Added code that shows the problematic client request.

the_mackster
  • 153
  • 2
  • 9
  • 2
    Does this answer your question? [Access from origin 'https://example.com' has been blocked even though I've allowed https://example.com/](https://stackoverflow.com/questions/70353729/access-from-origin-https-example-com-has-been-blocked-even-though-ive-allow) – jub0bs Mar 25 '23 at 08:57
  • 1
    Also, be careful regarding the scheme of allowed origins. Different schemes (`http` vs `https`) imply different origins, which may cause CORS to fail. Besides, you don't want to allow `http` origins in your CORS configuration because [it typically exposes your users to MitM attacks](https://www.youtube.com/watch?v=wgkj4ZgxI4c&t=21m). – jub0bs Mar 25 '23 at 08:59
  • @jub0bs this might be an issue. I wanted to avoid `https` so I wouldn't have to setup TLS (me being lazy), but perhaps that is what I ought to do. And *thank you* for linking that post! My origin had a trailing slash too.... – the_mackster Mar 25 '23 at 21:31
  • Seems like your issue is resolved, Can you share yours solution as answer so that it helps others too? – Roopa M Mar 27 '23 at 11:38
  • @RoopaM It actually isn't resolved. Removing the trailing slash did not fix the CORS error. – the_mackster Mar 28 '23 at 22:28
  • @jub0bs Removing the trailing slash did not fix the issue... – the_mackster Mar 28 '23 at 22:29
  • Can you check this [link1](https://stackoverflow.com/a/68950052/18265570) & [link2](https://stackoverflow.com/a/57959850/18265570) – Roopa M Mar 29 '23 at 07:02
  • @the_mackster You should remove the trailing slash from your configuration code in your question, as it likely to raise eyebrows from other potential answerers. Incidentally, listing `Cookie` in the `Access-Control-Expose-Headers` and `Access-Control-Allow-Headers` is pointless; you can safely drop it from those lists. Regarding your issue, you should show the client code responsible for sending the problematic request. – jub0bs Mar 29 '23 at 08:33
  • As mentioned in an earlier comment, you need to allow `https://shoedawg-client-dot-website-255218.uc.r.appspot.com` instead of `http://shoedawg-client-dot-website-255218.uc.r.appspot.com`. Besides, why are you including a `Content-Type` header to a `GET` request? Such requests are not meant to have a body. – jub0bs Mar 29 '23 at 21:50
  • @jub0bs As for your first point, the code actually does use `https`; I simply forgot to reflect that in my info above (I will change that now). As to your second point, I have no good answer. I am a web noob lol. Learning as I go. – the_mackster Mar 29 '23 at 21:55
  • @RoopaM I have looked at both of those links. Link #1 offered a bunch of reading material that I have mostly dug through and haven't found anything. Link #2 offered an alternative approach with `corsOptions` and whitelisting; tried that method to no avail. – the_mackster Mar 29 '23 at 22:01

0 Answers0