Login/out react routing and expressjs routing - node.js

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

Related

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

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.

How does serving different pages in React works?

Let's say I have a react projects, and an Express server for serving my project, as follows(After building the project):
]
This way, only the index.html is served, isn't it? If the user routes to a different page, how is that page sent to him?
Routing in javascript is managed by using HTML5 push state. So every time you click a link and go to another route, the browser history and push state is being used. That's the basis for routing in almost of all the single page applications.
Until and unless you refresh the page, your request doesn't go to the server. Hence, index.html is served only once and after that the router (here the react-router) takes over and manages the routing in url using the history API of the browser.
Hope this helps !
That is done using react-router which manages the routing using the browser's History API.
This style of a website is called a single page application as opposed to a multi page application where the server sends different pages depending on the url you route to.
you can use react-router-dom like this
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/someurl" component={ComponentForSomeUrl}
</Switch>
and render it with BrowserRouter
but you can use something like history.push, in my opinion react-router-dom is really simple and better than react-router
you don't need to send html file to specific route, in case of react express is used for building API (in most cases)
In you React folder you want to do npm install --save react-router-dom.
So inside the React Router family of libraries, there is a couple of different dependencies you can possibly install.
Ensure you never install React Router by itself.
The react-router library as it is published on npm is the core library of everything inside the React Router general project.
So react-router has some core navigational logic inside of it. It decides how to work with React, how to change content out depending on different rules and some other low-level logic.
To gain some actual implementation as it works specifically in the browser, install react-router-dom.
So anytime you want to use React Router on a project to handle navigation, always install react-router-dom, not react-router.
There are other similarly named projects that you might think you need as well, react-router-native for use inside of React Native projects.
In web applications we make use of react-router-dom, we are not making native mobile apps.
React-router-native is for native mobile applications only.
For the browser you always want react-router-dom as opposed to react-router-native
So perhaps in your App.js component you want to set something up that looks like this:
import React from "react";
import { BrowserRouter, Route } from “react-router-dom”;
const App = () => {
return <div>App</div>;
};
export default App;
I also recommend if you are new to React Router to get familiar with it by setting up something temporary like so:
import React from "react";
import { BrowserRouter, Route } from “react-router-dom”;
const PageOne = () => {
return <div>PageOne</div>;
};
const PageTwo = () => {
return <div>PageTwo<button>Click Me</button></div>;
};
const App = () => {
return (
<div>
<BrowserRouter>
<div>
<Route path=“/” exact component={PageOne} />
<Route path=“/pagetwo” component={PageTwo} />
</div>
</BrowserRouter>
</div>
);
};
Visit your localhost:3000 and then your localhost:3000/pagetwo, check out how all that is working.
When we visit a page called localhost:3000 and we type that address into the url it loads up the application.
React Router itself does not care about that entire url, instead React Router only cares about all the characters that are listed after the domain name or port definition.
Localhost:3000 is interpreted as being localhost:3000/
If I go to localhost:3000/ it still loads up my application.
Now I have other examples here if I go to localhost:3000/pageone, React Router only cares about everything after the port and domain
Same thing if I went to airbnb.com/listings/spain react router would only consider /listings/spain when deciding what content to render to the screen.
Notice in the example above I created an instance of BrowserRouter, well BrowserRouter creates an object of its own known as the history object.
This history object is going to look at the URL inside the address bar and extract just that portion of the URL that react router cares about.
The history object is then going to communicate that path over to BrowserRouter who communicates that path to both Route components and those Route components decide whether to show themselves or hide themselves depending on the path that the user is visiting and the path property that it was passed when it was created.

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.

Providing a React Router component with state variables after being redirected from the server (using Redux for state-handling)

I am building a client application using React and Redux, with server-side API endpoints set-up using Node.
For one feature I would like to send out a token in an email, then upon clicking a link (something like website.com/token?email=dave.example#something.com&token=3ad56gyhg) verify their token/email using a server-side API, before redirecting them to a particular page in React (using React Router).
I anticipate the Node API would look something like this:
app.get('/token', (req, res, next) => {
//code here.....
//goes and checks the email and token code match what is in the database
if (success) {
res.redirect('/welcome');
}
}
Once I've redirected to the appropriate React router endpoint, how do I provide state/props relevant to the user to any components? For example, I might want to use their email address on a page once their token has been verified.
ReactDOM.render(
<Provider store={store}>
<Router history={hashHistory}>
<Route component={App}>
<Route path="/" component={EntryPoint} />
<Route path="/welcome" component={WelcomeContainer} />
</Route>
</Router>
</Provider>,
document.getElementById('root')
);
Would I have to go down the isomorphic route and create a store on the server? Would the component need to go back and get an 'initial state' from the server?
You have a static HTML/CSS/JS server and you have a Node API. In this case, you can't 'template' the HTML that you send to the client. That means that you can only pass data to your react application via URL params.
app.get('/token', (req, res, next) => {
//code here.....
//goes and checks the email and token code match what is in the database
if (success) {
res.redirect(`/welcome/${encodeURIComponent(email)}`);
}
}
Then when your component loads, check for the query param:
ReactDOM.render(
<Provider store={store}>
<Router history={hashHistory}>
<Route component={App}>
<Route path="/" component={EntryPoint} />
<Route path="/welcome/:email" component={WelcomeContainer} />
</Route>
</Router>
</Provider>,
document.getElementById('root')
);
Alternative:
/token redirects to your webapp.
Your react application now picks up the email & token params and then makes an API request to /verify/token with the email & token parameters.
Your application handles the API request (which returns a success/fail) and then redirects internally to /welcome.
This is the way I've usually done this. The key is to make sure that when the user clicks on the verify link, they are taken directly to the webapp. The webapp does the API verification business.

react + routing + security

I am building a web application with React and react-router and I would like to protect some routes of my React application with an existing external access management infrastructure (OpenAM).
I would like to protect the http://web.example.com:8080/myapp/#/user-profile url which means that only logged users can have access to this route.
I have some other routes which are public and any user can open them, for example http://web.example.com:8080/myapp/#/welcome.
The Access Management protects urls and if a user wants to open a protected url then a login form is shown by Access Management and after a successful login the original requested url will be displayed.
The problem here is that react-router adds the routing info after the '#' character and the above mentioned two different urls are same from the access manager point of view because they refer to the same web resource (/myapp). The different between these two urls appear after the '#' character.
I need to have real urls without '#' chars like this:
http://web.example.com:8080/myapp/user-profile
http://web.example.com:8080/myapp/welcome
Is there any way to use real url mappings with React?
Do you guys have any idea or workaround how to use real url routes with react?
Thanks.
UPDATE:
This is my code. The urls in the web browser look nice but I get a "about did not match any routes" error. Requested url: http://web.example.com:8080/myapp/about
import {Router, Route, IndexRoute, useRouterHistory} from 'react-router';
import {createHistory} from 'history';
const browserHistory = useRouterHistory(createHistory) ({
basename: '/myapp/'
});
ReactDOM.render(
<Router history={browserHistory}>
<Route path="/" component={MainLayout}>
<IndexRoute component={Home} />
<Route path="about" component={About} />
</Route>
</Router>
)
You could use browserHistory, as suggested in the comments, to get rid of the #-sign. If you want the root of your react-application to be /myapp/ instead of / you can try:
import {Router, Route, useRouterHistory} from 'react-router';
import {createHistory} from 'history';
const browserHistory = useRouterHistory(createHistory)({basename: '/myapp/'});
ReactDOM.render(
<Router history={browserHistory}>
...
</Router>
)

Resources