Implement csurf to node apollo server and SSR react

Viewed 19

I have an SSR react application with nodejs backend, and i can't implement csurf protection. I read a thousands of articles but can't figure out where or how should i put the protection. Should i implement a GET method with one endpoint just for csrf both in backend and frontend?

If anyone have an idea that would be really helpful for me.

Backend: index.js

  const port = 9004;
  const app = new express();
  app.use(cookieParser())
  app.use(compression());
  app.use(cors({ origin: '*', credentials: true}));
  app.use(jwtMiddleware());
  app.use(bodyParser.urlencoded({ extended: false, limit: '200mb' }));
  app.use(bodyParser.json({ limit: '200mb' }));
  const csrfProtection = csrf({ cookie: true })
  app.use(csrfProtection)

  const getCsrf = function (req, res, next) {
    const token = req.csrfToken()
    res.cookie('csrf-token', token)
    res.send({ csrfToken: token });
    next()
  }
  
  app.use(getCsrf)

  withApollo(app, schema, resolvers).listen(port, () => {
    console.log(`GRAPHQL Server is now running on ${port}/graphql`);
  });

Apollo middleware:

export default (app, typeDefs, resolvers) => {
  const apollo = new ApolloServer({
    typeDefs,
    resolvers,
    csrfPrevention: true,
    context: ({req}) => ({
      user: req.user,
      context: req.headers['x-context'],
      domain: req.headers['x-domain']
    }),
  });

  apollo.applyMiddleware({app});
  return app;
};

React server:

export default (context, io) => {
  const app = new express();

  const upload = multer();
  const shouldCompress = (req, res) =>
    req.headers["x-no-compression"] ? false : compression.filter(req, res);

  app.use(contextMiddleware(context));
  app.use(cookieParser());
  app.use(bodyParser.json({ limit: "200mb" }));
  app.use(bodyParser.urlencoded({ limit: "200mb", extended: true }));
  app.use(upload.any());
  app.use(robots({ UserAgent: "*", Disallow: "/" }));
  app.use(cors({ origin: "*", methods: "GET,HEAD,PUT,PATCH,POST,DELETE",credentials: true }));
  app.use(compression({ level: 2, filter: shouldCompress }));
  app.use(
    session({
      secret: "2C44-4D44-WppQ38S",
      cookie: { maxAge: 2628000000 },
      resave: true,
      saveUninitialized: true,
    })
   );

  app.use(
    "/public",
    express.static(path.resolve(process.cwd(), "public"), { maxAge: 3600000 })
  );
  app.use("/assets", express.static(path.resolve(process.cwd(), "assets")));
  app.use("/api/admin", adminController);
  app.use("/api/login", loginController);

  app.get("*", async (req, res) => {
    const branch = matchRoutes(appRoutes, req.path);
    const hasUser = hasSessionUser(req.session);
    const hasToken = hasUser ? await validateSessionUser(req) : false;
    if (req.path === "/") return res.redirect("/login");
    if (req.path === "/login" && hasUser && hasToken)
      return res.redirect("/admin");

    const store = createStore(req);
    const promises = branch.map(({ route }) =>
      route.loadData ? route.loadData(store) : Promise.resolve(null)
    );

    Promise.all(promises).then(() => {
      const reqContext = {};
      const html = render(req, store, reqContext);
      if (reqContext.notFound) res.redirect("/error");
      return res.send(html);
    });
  });

  return app;
};

In frontend the graphql connected post method:

const request = ({ body, headers = {}, baseUrl } = {}) => 
  axios({
    method: "POST",
    baseURL: baseUrl,
    url: 'graphql',
    data: body,
    headers,
    withCredentials: true,
  }).catch(error => {
    if (!error.response) throw error;
    return error.response;
  });

const apiWrapper = async ({ body, headers = {}, baseUrl = null }) => {
  const result = await request({ body, headers, baseUrl }).catch(error => ({
    status: undefined,
    data: { code: 'NETWORK_ERROR', message: error.message },
  }));
  return result
}

export default apiWrapper;
0 Answers
Related