See update below...
My React front-end is able to call upon my Node back-end API. However, when an external site calls upon my API, it receives an error (for POST
requests but not for GET
requests).
App setup includes:
const express = require("express");
const cookieParser = require("cookie-parser");
const csurf = require("csurf");
var cors = require("cors");
var corsOptions = {
origin: process.env.CORS_ORIGIN_URL.split(","),
credentials: true,
exposedHeaders: ["set-cookie"],
};
app.use(cors(corsOptions));
app.use(
cookieParser(process.env.COOKIE_SECRET, {
sameSite: true,
httpOnly: true,
secure: process.env.NODE_ENV === "production",
maxAge: process.env.COOKIE_MAX_AGE,
})
);
// If I comment out the next 5 lines, external services CAN call upon my API
app.use(csurf({ cookie: true }));
app.use(function (req, res, next) {
res.cookie("XSRF-TOKEN", req.csrfToken());
next();
});
app.use("/api/csrf", (req, res) => {
return res.status(200).json({
status: true,
csrfToken: req.csrfToken(),
});
});
app.use("/api", api);
If I comment out the CSRF part (the 5 lines) of the setup, then external services ARE able to call upon my API. How should I adjust this CSRF setup to make my application work also with "external" API calls?
Update
I've updated the CSRF configuration to the setup below. Would this be a secure setup? The idea is to use CSRF except for endpoints that need to be reachable to external services. res.cookie("XSRF-TOKEN", req.csrfToken());
no longer seems to work.
app.use(
cookieParser(process.env.COOKIE_SECRET, {
sameSite: true,
httpOnly: true,
secure: process.env.NODE_ENV === "production",
maxAge: process.env.COOKIE_MAX_AGE,
})
);
var csrf = csurf({ cookie: true });
app.use(function (req, res, next) {
if (req.url === '/api/mollie_webhook') {
return next();
} else {
app.use(csrf);
// I think this is not allowed, since we already have app.use 4 lines up, but don't know how else to do this...
// it correctly gets to this point
res.cookie("XSRF-TOKEN", req.csrfToken());
// but then not to this point
app.use("/api/csrf", (req, res) => {
return res.status(200).json({
status: true,
csrfToken: req.csrfToken(),
});
});
}
});
app.use("/api", api);
CodePudding user response:
That's not the purpose of CSRF protection. CSRF protection is to prevent direct posting of data to your site. In other words, the client must actually post through an approved path like filling data to a form and submit it.
So an api will precludes Csrf, because its purpose is generally to allow 3rd party entities to access and modify data on your site . So as a rule any API view should not be use CSRF. And you can protect your endpoint in various ways such as oauth and there are other best practices too
CodePudding user response:
You just need to declare the Web Hook route before you add the CSRF middleware. You will however, need to enable CORS on this route.
const express = require('express');
const cors = require('cors');
const csurf = require('csurf');
const cookies = require('cookie-parser');
const app = express();
const shared = cors({
origin: 'https://api.mollie.com',
methods: 'POST'
});
app.use('/api/webhook', shared, (req, res) => {
res.sendStatus(200);
});
app.use(cookies(process.env.COOKIE_SECRET, {
sameSite: true, httpOnly: true,
secure: process.env.NODE_ENV === "production",
maxAge: process.env.COOKIE_MAX_AGE,
}));
app.use(csurf({ cookie: true }));
app.use("/api/csrf", (req, res) => {
res.json({
status: true,
csrfToken: req.csrfToken(),
});
});
app.listen(process.env.PORT);