How do I login protect a static html page in node/react? - node.js

Newer to node and react. So I have a static html page. The react on the prod server is run by nginx. I have this route protected by a login.
{!guest && <Route path="/" component={Home} />}
Home then contains the route to the virtual path which redirects.
<Route path="/virtual" render={() => {window.location.href="virtualworkouts.html"}} />
Someone can easily bookmark that page and circumvent the login entirely. How can I make it truly login protected? Something like...
app.get('/virtual', function(req, res) {
res.sendFile(__dirname + "/public/virtualworkouts.html");
});
Where that file is somewhere different than what nginx is serving.
Thanks!

So if I understand your question correctly. Somebody gets served an HTML file. If they go directly to the page they still get that file. Given that in the question you are using react-router and the file is still being served. You most likely need to check if the user is logged in on the server.
function isLoggedIn() {
if (someConditionToCheckUserIsLoggedIn)
return next();
else
res.redirect('/error');
}
app.get('/yourroute', isLoggedIn, function(req, res) {
// Code
});
This way if the user requests a certain page they won't receive the requested file unless they are logged in. Note the browser may still cache the file though.

Related

In a MERN app, if you have a app.get('/') on your server and a <Route path=/' > on your client, which one will trigger first or take priority?

Say you have a React client with a nodejs express backend. On the server you have a route set up such that app.get('/') serves a Home.html. On the React side you have react-router-dom set up like
<Route path='/' component= { NotHome } />
What file will be served/shown to the user, Home or NotHome?
There is a difference between server-side routing and the client-side routing.
If you are making a standard HTTP request for the page (e.g. you type the URL in your browser's address bar), the server-side routing will take effect (e.g. serve you Home.html)
But if your Home.html then references the JS containing the <Route /> tag, client-side routing takes effect. react-router-dom will render the NotHome component in that case.
The server route will take preference, in a react app using express, your main server file should have a single route like this:
app.get('/*', (req, res) => {
res.sendFile(path.join(__dirname, '/index.html'));
})
app.listen(8000, '0.0.0.0', (err) => {
if (err) {
console.log(err)
}
console.log('Listening on 8000')
})
The app.get('/*') is a catch all so that every get request e.g every address in the address bar of your browser will serve the index, which will in turn serve your bundle.js and render components based on your
Hope this helps.
Lloyd

React routing with express is working on page refresh

My project using express for server and React for frontEnd. Routes are like this
<Router >
<Switch>
<Route exact path="/" component={HomeContainer} />
<Route path="/women" component={SectionContainer} />
</Switch>
</Router>
To serve these routes my server js has
server.get('*', function(request, response) {
response.sendFile(path.resolve(__dirname, '../public', 'index.html'));
});
Page url http://localhost:3000/women is working only on page refresh, first time click on url is just changing the browser url with no page update. But on page refresh it is working perfectly fine.
Please suggest what i am missing.
I was having similar issue. I found HashRouter helpful than the BrowserRouter:
import { HashRouter as Router } from 'react-router-dom'
Using HasRouter will be working fine as it keeps state on every history data changes.

redux onEnter dispatch could work on server rendering?

i have a working client side app which uses redux-router.
i dispatch the initial state of user page from my api.
my routes file:
export default function ({ dispatch, getState }) {
function getUser(nextState, replaceState) {
dispatch(getUserData(nextState.params.id));
}
return (
<Route path="/" component={App}>
<Route path="user/:id" component={User} onEnter={getUser}/>
<Route path="*" component={NoMatch}/>
</Route>
);
}
}
so on the client it works great.
i would like to get my markup rendered-to-string after the getUserData dispatch is back with data.
thats my server matching and rendering (from the official server-rendering example):
app.use((req, res) => {
const store = reduxReactRouter({ routes, createHistory: createMemoryHistory })(createStore)(reducer);
const query = qs.stringify(req.query);
const url = req.path + (query.length ? '?' + query : '');
store.dispatch(match(url, (error, redirectLocation, routerState) => {
if (error) {
console.error('Router error:', error);
res.status(500).send(error.message);
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (!routerState) {
res.status(400).send('Not Found');
} else {
res.status(200).send(getMarkup(store));
}
}));
});
is it possible to get this working while using onEnter?
or it only fires on the browser?
would appreciate any help guys!
thanks
I don't understand - I assume getUser is async, rendering is always synchronous in React. You'd have to get the data ahead of time. Relying on onEnter is a nice idea but it wouldn't work.
Basically...
First: you need to expose your methods that grab data so they can be called on the server outside of flux. The action/dispatcher/store doesn't work well on request/response cylce and make sure you have a file with API calls.
Second: put all your routes in a JSON file, React Router (or whatever router) reads that JSON and adds the routes in a loop with their handler, the server-side code reads those routes and adds express routes for the same routes pointing to the method, each route in the JSON also contains a reference to the data the component needs to a real initial render (what async calls). You create an empty copy of the initial state object, each handler on the express side performs all the calls to the relevant API methods (from the JSON) and when a Promise.all resolves on "getting the data and filling it in the state" resolves you render,
The render now contains the relevant data.
Third, you need to figure out how to pass state to the server, how the user is logged in, what they can do and so on - I recommend having a second server act as a cache since rendering is CPU bound in React so you need caching. We cache based on route, user status, device and a few other basic things.
Fourth, you point your non-rendering server (the caching one) to the rendering one and forward requests, hopefully requests should hit the cache, you need to have the non-rendering server "fall back" to client-side rendering only if the rendering server fails. This also lets you "hot swap" the rendering server on deploy.
You also need some way to deliver the JavaScript itself from the rendering server (so there is a single source of truth) that shouldn't be too big of an issue.
There are a lot more delicate parts in the flow - but that's pretty much how we do it and we have sub-second full renders which I think is nice. Went down from 5 seconds when our site was mostly Angular and rendered on the client.

Setting up React Router with Express

I have a few questions on setting up React with Express.
First, I used the same route paths for both Express and React-Router. I thought these were supposed to match up. But when I navigate to a page, api/blogPosts, it just shows the JSON format of data that I fetched from MongoDB. That is, the Express route overrides the React view. I know that I could just modify the route path in React-Router (for example, without 'api' in front) so that there are different routes. Then it will show the React view as expected, while still making the api calls. But again, I thought that the route paths were supposed to match up. How should I handle this properly?
Second, I used express.Router() and I'm not sure this is necessary. When should I use express.Router() and when should I just use app.get, app.post, etc.
Third, when people discuss client side routing are they discussing things like React Router? When people discuss server-side routing are they just referring to making api calls to the database like the apiRouter calls?
routes.js
<Route component={App}>
<Route path='/' component={Home} />
<Route path='/api/blogPosts/create' component={Create} />
<Route path='/api/blogPosts/:blogPostId' component={BlogPost} />
</Route>
server.js
var apiRouter = express.Router();
apiRouter.route('/blogPosts')
.post(function(req, res) {
var blogPost = new BlogPost();
blogPost.postbody = req.body.postbody;
blogPost.save(function(err) {
if (err) {
return res.send(err);
}
res.json({ message: blogPost.postbody + "created"})
});
})
.get(function(req, res) {
BlogPost.find(function(err, posts) {
if (err) return res.send(err);
res.json(posts);
});
});
apiRouter.route('/blogPosts/:blogPostId')
.get(function(req, res) {
BlogPost.findById(req.params.blogPostId, function(err, blogPost) {
if (err) return res.send(err);
res.json(blogPost);
})
});
app.use('/api', apiRouter);
So from my experience React Router is for client side routing for a single page application. Meaning it uses the browser's history api to make it appear like the browser is going to different routes without actually leaving the original page. The express routing is server side routing and is used for either interacting with some API or database, like you mentioned, or for serving a new page at that URL.
As far as when you should use expressRouter vs app.get I'd say try to use the Router whenever possible as it is good practice. There's a pretty decent explanation here Difference Between app.use() and router.use() in Express
Now finally, if you want to do server side rendering with react router look here: Client Routing (using react-router) and Server-Side Routing
you need to add one non api route to serve your SPA(single page app)
create another express router and then add this
router.use('*',function(req,res,next){
var indexFile = path.resolve(__dirname,'./public/index.html');
res.sendFile(indexFile);
})
or you can just put the index.html in your public folder and load your SPA from that index page.

Login/out react routing and expressjs routing

What im trying to do is Have a Log In page, a Sign up page. The login and signup work fine, they just perform whatever was written in express using passport. However what I want to do is make a route '/dashboard'. I did so and it's just a dashboard component made up of other components like a navbar and body/content. How do I write it in react router in such a way that it only allows me to access the route /dashboard if the user is authenticated. In express I would write a function like this :
function isLoggedIn(req, res, next) {
//if user is authenticated in the session, carry on
if (req.isAuthenticated())
return next(); //cuz we want to move on incase we're stuck here
//if they arent redirect them to the home page
res.redirect('/');
}
However I didn't even write in the dashboard route in express. I only did it in react-router. My component hierarchy is like this right now
<App/>
<Dashboard/>
<NavBar/>
<NavMenu/>
<Body/>
<Login/>
<Signup/>
<About/>
And my routing is like this :
<Route>
<Route name ="signup" path="/signup" handler={Signup} />
<Route name ="login" path="/" handler={Login} />
<Route name ="app" path="/dashboard" handler={App} >
<Route name ="logout" path="/logout" handler={NavMenu} />
</Route>
</Route>
I don't understand if i'm grasping the concept right, but from what the webpage is displaying it doesn't seem like it. So basically at localhost:3000/ comes up the log in page, and I can toggle between that and the signup page completely fine, when the log in button is hit it uses express to login (it can use react-router to do it as well correct?), on success it goes to /dashboard (res.redirect('/dashboard') in express). It also seems as routes handled by react router has that single page app feel, whereas express it feels like i'm going to a new webpage (I'm guessing that happens because of react-router just changing the url and rendering the component we want?). In my NavMenu component I link the logout button Logout however the url changes to localhost:3000/logout and nothing happens.
I nested it under login so to get to dashboard and it's routehandler it has to go through the login.
<Login>
<App>
<RouteHandler>
<dashboard../>
<data../>
<etc../>

Resources