How to restrict access to routes in react-router?
There is (now?) an example of this in React Router 4's docs for Redirect
import { Route, Redirect } from 'react-router'
<Route exact path="/" render={() => (
loggedIn ? (
<Redirect to="/dashboard"/>
) : (
<PublicHomePage/>
)
)}/>
Update (Aug 16, 2019)
In react-router v4 and using React Hooks this looks a little different. Let's start with your App.js
.
export default function App() {
const [isAuthenticated, userHasAuthenticated] = useState(false);
useEffect(() => {
onLoad();
}, []);
async function onLoad() {
try {
await Auth.currentSession();
userHasAuthenticated(true);
} catch (e) {
alert(e);
}
}
return (
<div className="App container">
<h1>Welcome to my app</h1>
<Switch>
<UnauthenticatedRoute
path="/login"
component={Login}
appProps={{ isAuthenticated }}
/>
<AuthenticatedRoute
path="/todos"
component={Todos}
appProps={{ isAuthenticated }}
/>
<Route component={NotFound} />
</Switch>
</div>
);
}
We are using an Auth
library to check if the user is currently authenticated. Replace this with your auth check function. If so then we set the isAuthenticated
flag to true
. We do this when our App first loads. Also worth mentioning, you might want to add a loading sign on your app while the auth check is being run, so you don't flash the login page every time you refresh the page.
Then we pass the flag to our routes. We create two type of routes AuthenticatedRoute
and UnauthenticatedRoute
.
The AuthenticatedRoute.js
looks like this.
export default function AuthenticatedRoute({ component: C, appProps, ...rest }) {
return (
<Route
{...rest}
render={props =>
appProps.isAuthenticated
? <C {...props} {...appProps} />
: <Redirect
to={`/login?redirect=${props.location.pathname}${props.location.search}`}
/>}
/>
);
}
It checks if isAuthenticated
is set to true
. If it is, then it'll render the desired component. If not, then it'll redirect to the login page.
The UnauthenticatedRoute.js
on the other hand looks like this.
export default ({ component: C, appProps, ...rest }) =>
<Route
{...rest}
render={props =>
!appProps.isAuthenticated
? <C {...props} {...appProps} />
: <Redirect to="/" />}
/>;
In this case, if the isAuthenticated
is set to false
, it'll render the desired component. And if it is set to true, it'll send you to the homepage.
You can find detailed versions of this on our guide - https://serverless-stack.com/chapters/create-a-route-that-redirects.html.
Older version
The accepted answer is correct but Mixins are considered to be harmful (https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html) by the React team.
If somebody comes across this question and is looking for the recommended way to do this, I'd suggest using Higher Order Components instead of Mixins.
Here is an example of a HOC that'll check if the user is logged in before proceeding. And if the user is not logged in, then it'll redirect you to the login page. This component takes a prop called isLoggedIn
, that is basically a flag that your application can store to denote if the user is logged in.
import React from 'react';
import { withRouter } from 'react-router';
export default function requireAuth(Component) {
class AuthenticatedComponent extends React.Component {
componentWillMount() {
this.checkAuth();
}
checkAuth() {
if ( ! this.props.isLoggedIn) {
const location = this.props.location;
const redirect = location.pathname + location.search;
this.props.router.push(`/login?redirect=${redirect}`);
}
}
render() {
return this.props.isLoggedIn
? <Component { ...this.props } />
: null;
}
}
return withRouter(AuthenticatedComponent);
}
And to use this HOC, just wrap it around your routes. In case of your example, it would be:
<Route handler={requireAuth(Todos)} name="todos"/>
I cover this and a few other topics in a detailed step-by-step tutorial here - https://serverless-stack.com/chapters/create-a-hoc-that-checks-auth.html