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.
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.