How to defend routes in reactJS - reactjs

I came across a problem which I dont know how to fix. I dont even know where to start.
The problem is next : I have url "localhost:4000/" If I will type url "localhost:4000/homepage" It will redirect me to my home page,But i want to restrict this access if token is not preserved.
Here are my routes :
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import LogIn from "./components/LogIn/Login";
import Register from "./components/Register/Register";
import Homepage from './components/HomePage/HomePage'
import Profile from './components/Profile/Profile'
import Posts from './components/addPostUI/Posts'
import "./app.css";
function App() {
return (
<Router>
<div className="App">
<Switch>
<Route path={"/"} exact component={LogIn} />
<Route path={"/login"} component={LogIn} />
<Route path={"/registration"} component={Register} />
<Route path={'/homepage'} component={Homepage} />
<Route path={'/profile'} component={Profile} />
<Route path={"/posts"} component={Posts} />
</Switch>
</div>
</Router>
);
}
export default App;
Any suggestions on how to restrict access and defend my routes?

There are many ways to restrict your Routes conditionally. The basic approach you can follow here is:
<Router>
<Switch>
<Route
path='/homepage'
render = {() => {
if(accessToken || localStorage.getItem('accessToken')) {
return <Homepage />;
} else {
return <Redirect to='/login' />;
}
}}
/>
</Switch>
</Router>
You can follow along with these steps whenever there is a need for conditional routing.

First determine what you want to authenticate users by, then you can do the following inside the page you want to protect, before the page renders.
if (failed access condition) return <Redirect to='/some route you want to redirect to' />
Be sure to import Redirect like this:
import { Redirect } from 'react-router-dom'
Also Consider
If you have a database, be sure to restrict access to the data based on the access token as well and don't depend on client side code to protect data.

Related

How do I convert BrowserRouter react code for nextjs?

I'm trying to conditionally route the user based on whether they are logged in or not which is checked by my global state variable user. I'm trying to implement this react code into Next.js but I am getting a document reference error. How would I be able to perform the same logic in this code but having it Next.js oriented? Essentially, I don't want to use BrowserRouter.
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import { useAuthContext } from 'hooks/useAuthContext';
import HomePage from './home';
import ProductPage from './product';
import LoginPage from './login';
import SignupPage from './signup';
export default function IndexPage() {
const { user } = useAuthContext()
return (
<div>
<BrowserRouter>
<div>
<Routes>
<Route
path="/"
element={<HomePage />}
/>
<Route
path="/login"
element={!user ? <LoginPage /> : <Navigate to="/" />}
/>
<Route
path="/signup"
element={!user ? <SignupPage /> : <Navigate to="/" />}
/>
<Route
path="/product"
element={!user ? <ProductPage /> : <Navigate to="/" />}
/>
</Routes>
</div>
</BrowserRouter>
</div>
);
}
Nextjs has a file based routing system.
If you want to implement protected routing system then it is preferable to use nextjs middleware. (https://nextjs.org/docs/advanced-features/middleware)
Next.js solves those complications. As already mentioned, routings are based on directory. You can refer to the next.js document here.

Match only specific ids in the url

I am trying to setup the PageNotFound. The 2nd route should match only for ids ['1', '2', '3']; (these id values hwoever i get after the api call. So i save the values in redux store from inside the Home component). Now when i try and access a incorrect id then it should redirect to PageNotFound. If id in url is lets say 4 it should redirect to PageNotFound.
Do i impletement this is User component when it gets redirected or is there a way to set it up in the below App.js file itself ?
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Home from "./Pages/Home";
import User from "./Pages/User";
import PageNotFound from "./Pages/PageNotFound";
import "./App.css";
function App() {
return (
<div className="App">
<Router>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/user/:id" exact component={User} />
<Route path="*" exact component={PageNotFound} />
</Switch>
</Router>
;
</div>
);
}
export default App;
You may create generic component for validation purpose.
Solution should be like this
<Route path="/user/:id" exact component={MyCheckComponent} />
MyCheckComponent.jsx
const MyCheckComponent =(props) => {
const {id} = useParams()
const allowedIds = useSelector(s => s.user.allowedIds)
return allowedIds.includes(id) ? <User {...props} /> : <Redirect to="/not-found"/>
}
At the end you may eliminate MyCheckComponent using render prop of the route.

Reactjs - How to re-direct to default home page

I am trying to set redirection to default page, but not works. what is the correct way to do this?
const routes = [
{
path:"/",
component:Home,
extract:"true",
redirect:"/home"
},
{
path:"/home",
component:Home
},
{
path:"/service",
component:Service
}
];
html:
<div>
<Router>
<Header />
{routes.map((route) => (
<Route
key={route.path}
path={route.path}
component={route.component}
/>
))}
</Router>
Assuming you are using react-router-dom, I can see one definitive issue with some additional concerns. You may only need to make one change, but I would advise reviewing the rest of your code to ensure the best routing config.
Main Issue: You have to add a redirect to '/home' if you want that component to be the first page to be loaded. This is because when the app is rendered, it sees the default path as '/'.
<Redirect exact from="/" to="/home" />
<Route path="/home">
<Home />
</Route>
While this may solve your problem by itself, here is a more comprehensive solution that should be beneficial to compare your current code against.
import {
BrowserRouter as Router,
Switch,
Route,
Redirect
} from "react-router-dom";
import home from "./Home";
import service from "./Service";
import header from "./Header";
function App () {
return (
<div>
<Router>
<Header />
<hr />
<Switch>
<Redirect exact from="/" to="/home" />
<Route path="/home">
<Home />
</Route>
<Route exact path="/service">
<Service />
</Route>
</Switch>
</Router>
</div>
);
}
export default App;
As you can see, I would recommend moving the Header outside of the Switch statement and then applying the redirect from / to /home.
Also, please note this configuration is simply an example. It depicts a situation where you are exporting App as a component and does not account for login authorization, so certain aspects of your code may vary.

Prevent Navigation Component Rendering on Single Route but Render on every other Route

So I have a single Route /reminder, where I do not want my Navigation component, but I want the Navigation component to render on every other Route and I'm not sure how to go about this?
This is my App.js where I am using react-router-dom for my routing.
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Navigation from "./components/navigation/Navigation";
import Reminder from "./components/reminder/Reminder ";
const App = () => {
return (
<>
<Switch>
<Route path="/reminder" component={Reminder} exact />
</Switch>
<Navigation />
<MainContent>
<Router>
<Route path={"/"} component={Dashboard} exact />
</Router>
</MainContent>
</>
);
}
I had a look around for similar issues, but it's mostly for authenticated pages, this isn't for an authenticated page.
I don't want to go down the route of having to add the Navigation component into each of my 21 current routes, it seems like there should a better method?
With my current code above, it renders the Reminder component but still renders the Navigation component.
You can access the match data using a custom hook from react-router. All you need to do is
import { useRouteMatch } from "react-router-dom";
function App() {
let match = useRouteMatch("/reminder");
// Do whatever you want with the match...
return (
<>
<Switch>
<Route path="/reminder" component={Reminder} exact />
</Switch>
{!match && <Navigation />} <---Conditional rendering
<MainContent>
<Router>
<Route path={"/"} component={Dashboard} exact />
</Router>
</MainContent>
</>
);
}
Also, while I didn't change this in your example, it's not generally a good idea to have a route outside of the router component. I'm actually a little surprised react-router-dom didn't throw an error for you.

Use layout for certain routes in React

I need to maintain a section of my app available for all pages (most of the pages actually)
to do that I wrapped the routes in a Layout
<Router>
<Route path="/login" exact strict component={Login}/>
<Layout>
<Route path="/list" exact strict component={List}/>
<Route path="/settings" exact strict component={Settings}/>
</Layout>
</Router>
the Layout component looks like this
class Layout extends Component {
render() {
return (
<React.Fragment>
<div>this box should be visible in all pages using layout</div>
<div>{this.props.children}</div>
</React.Fragment>
)
}
}
this work perfectly fine. the only poblem is when I go to /login
is rendering the Login component and also the Layout component.
I shouldn´t render the Layout if is not for one of the ¨protected¨ routes.
is there a way to achieve that without moving the layout inside of the List of Settings component?
the reason for this is because the box that´s fixed in all pages makes one API request and there´s no need to do that same API call for /list and /settings. I do it only once.
hope this makes sense to you. thank you!
update:
"react-router-dom": "^5.0.1"
I've done a fair bit of research on the subject and came up with this. I have it set up in my routes:
import React from 'react'
import { BrowserRouter as Router, Redirect, Route, Switch } from 'react-router-dom'
// import PublicRoute, PrivateRoute, Login, Private
<Router>
<Switch>
<PublicRoute exact path='/login' component={Login} />
<PrivateRoute path='/private' component={Private} />
</Switch>
<Router>
Then in my privateRoute.js:
import React from 'react'
import { Redirect, Route } from 'react-router-dom'
// import useAuth and PrivateLayout
export default ({ componet: Component, ...rest }) => {
const { token } = useAuth() // wrapper I wrote to handle auth
if (!token) return <Redirect to='/login' />
return (
<Route {...rest} render={props => (
<PrivateLayout><Component {...props} /></PrivateLayout>
)} />
)
}
And my publicRoute.js is similar but without authentication. My layouts are a simple wrapper, all you need to do is include children:
export default ({ children }) => {
return (
<>
<Topbar />
<Sidebar />
{children}
</>
)
Note: For those who don't know <> is the shorthand for <React.Fragment>.

Resources