Reactgo: App Only Routes

Created on 16 Mar 2017  路  7Comments  路  Source: reactGo/reactGo

Currently if you want to access all the topic, or create new ones all you need to do is read the code base and run some REST calls against the /topic endpoints in the server/init/routes.js file.

By default routes like this should only be accessible by the application rather than available to anyone who looked at this one file.

Trying to think of a good way to do this... possibly some kind of Application key? Thought of trying to use a check source location but each client would be different... plus if any idiot reads through the compiled source They would see a client side API key... what about tying it to the passport? What would that look like?

Pros - Prevents non-public API calls via exposed routes for application.

Cons - Another layer of complexity.

question

All 7 comments

This can be achieved by require a anti csrf token being passed with each request. Something like:

  1. On server, pass a csrf token to the client
  2. On client for subsequential API requests, pass the token
  3. On server, validate the token

The token (cookie) can be made more secure, with some settings such as same-origin

We're trying to clean up the project and close issues.
The solution that was posted is very viable and good. Basically you should have authentication implemented.
In case you still have an issue with this, please comment here or open a new issue.

Question:

In a fork of this repo, I have a very simple csrf express middleware which uses the express csruf middleware, that would be easy to add to this repo.

Usage

app.use('/api', csrfProtectionMiddleware, apiMiddleware)

Is there any interest in a PR adding this?

You would also need to change the front-end to include the csrf token on the requests.
It can add a layer of security and it will help but it does require a bit more working on it.
It is probably recommended to add CORS protection to verify the origin of the request. When would you decide to refresh the csrf token?
Also, it we go to the path of JWT then we might not need the csrf token. Check this and this

I actually added this to my app with out using CSRF. By adding the following middleware to my routes. This allows me to be selective on which routes have the login check versus the blanket method the CSRF token uses.

server/init/routes.js

function ensureLoggedIn(req, res, next) {
  /*
   * This checks is the isAuthenticated function exists and it's value
   * If the request came from a user (browser) with an active session the route will execute
   * If the request came from the server with a server authorization header it will also work
  */
  if ((req.isAuthenticated && req.isAuthenticated()) ||
    req.headers.server_authorization === sessionSecret) {
    next();
  } else {
    res.status(401).send({
      success: false,
      message: 'You need to be authenticated to access this page!'
    });
  }
}
// Example
app.post('/my-list', ensureLoggedIn, myController.list);

The tricky part was getting it to work with the middleware. If you refresh the page the server side axios calls in the fetch data promises don't contain the user's session which is why I added the server_authorization header. As long as this head is set and unset it'll never been seen on any client just on the requests where the server is talking to itself so we don't need to reuse our code.

update "match" portion in server/render/middleware.js

match({routes, location: req.url}, (err, redirect, props) => {
    if (err) {
      res.status(500).json(err);
    } else if (redirect) {
      res.redirect(302, redirect.pathname + redirect.search);
    } else if (props) {
      // This method waits for all promises to resolve before returning to browser
      store.dispatch({ type: types.FETCH_DATA_REQUEST });

      // Intercept the axios requests and add a session secret to the header.
      const myInterceptor = axios.interceptors.request.use((c) => {
        const config = c;
        config.headers.common.Server_Authorization = sessionSecret;
        return config;
      });

      fetchDataForRoute(store.dispatch, props)
      .then((response) => {
        response.forEach((promiseResponse) => {
          // console.log(promiseResponse);
          // In event of error data fetcher should return appropriate error response.
          store.dispatch({ type: promiseResponse.type, data: promiseResponse.data});
        });
        const html = pageRenderer(store, props, req);
        res.status(200).send(html);
      })
      .catch((error) => {
        console.error(error);
        res.status(500).json(error);
      });
      // Remove the axios interceptor
      axios.interceptors.request.eject(myInterceptor);
    } else {
      res.sendStatus(404);
    }
  });

I may still add anti CSRF at some point but for now this is what I needed.

great :)
I believe that you can simplify this and use the regular Cookie header on the SSR side instead of a custom header name. Check https://github.com/reactGo/reactGo/pull/937

@slavab89 Thanks again this is what I was trying to do but could figure out how to pass the cookie to the axios call from server side. Thanks for knowing this boilerplate inside and out ;)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

psimyn picture psimyn  路  8Comments

gsccheng picture gsccheng  路  6Comments

jrodl3r picture jrodl3r  路  7Comments

choonkending picture choonkending  路  8Comments

jrodl3r picture jrodl3r  路  6Comments