Handling Private and Public routes on the client-side

We have many different components in our application and we need some of them (Users list, Todo list) to be available for authorized users only, but some of the others (Login form) should be available for non-authorized users. It’s not really efficient to check this inside of every component, so we will create special containers to handle it on the Route level. The first one allows only guest access and redirects to the main page if the user is already logged in.

/**

* Router for only guest stuff like Login/Register

* If not guest - redirects to home

*/ class GuestRoute extends React.Component {

render() {

const {

isAuthenticated,

component: Component,

...props

} = this.props

return (

<Route

{...props}

render={props =>

!isAuthenticated

? <Component {...props} />

: (

<Redirect to={{

pathname: '/',

state: { from: props.location }

}} />

)

}

/>

)

}

} const mapStateToProps = ({ auth }) => {

const isAuthenticated = auth.isAuthenticated

return {

isAuthenticated,

}

} export default connect(mapStateToProps)(GuestRoute)

The second one is PrivateRoute which allows access only for authorized users, and redirect to the Login page is the user is not logged in.

/**

* Private route to navigate over private routes

* If not logged in - goes to login

* If not admin but required - throws an error!

*/ class PrivateRoute extends React.Component { componentDidMount() {

this.props.dispatch(verifyToken())

} logoutHandler() {

this.props.dispatch(logout())

} render() {

const {

isAuthenticated,

component: Component,

current_user,

...props

} = this.props

if (this.props.isLoading) {

return <Loading />

}

if (isAuthenticated && !current_user) {

return null

}

return (

<Route

{...props}

render={props =>

isAuthenticated

?

<main>

<Header

current_user={current_user}

logout={this.logoutHandler.bind(this)}

/>

<Component {...props} />

</main>

: (

<Redirect to={{

pathname: '/login',

state: { from: props.location }

}} />

)

}

/>

)

}

} const mapStateToProps = ({ auth }) => {

const current_user = auth.current_user;

const isAuthenticated = auth.isAuthenticated;

return {

isAuthenticated,

current_user,

isLoading: auth.isLoading,

}

} export default connect(mapStateToProps)(PrivateRoute)

On component Mount we check the authentication status by dispatching the verifyToken action, and then either pass current User profile to the state of our application, or redirect to the Login page. The isLoading flag is used for the transition state.

For this container we also add more features like a common Header with a welcome message and a link for Logout, which will be displayed on all pages when the user is logged in.



* Header with greetings and links

*

*/ /*** Header with greetings and links @param {object} props*/ const Header = (props) => {

return <header className="header">

<p className="header__greeting">

<span>

Welcome

<a className="header__link" href="">

{props.current_user.first_name}

</a>

</span>

</p>

<p className="header__logout">

<a className="header__link" href="" onClick={(e) => { e.preventDefault(); props.logout(); }}>

Logout

</a>

</p>

</header>

} export default Header

As you can see those Route Containers are higher order components which wrap a generic react-router Route component so we can use them inside our application routing in the main App file. All public and guest routes are here.

const App = () => {

return <Switch>

<GuestRoute exact path='/login'

component={LoginContainer}

/>

<PrivateRoute exact path='/' component={UserListContainer}/>

<PrivateRoute path='/todos/:userId'

component={TodoListContainer}

/>

</Switch>

};

Wrapping up

After going through all the source code examples, let’s see how our application actually works.

When we run our application and open the page in a browser — we’re not logged in and the check in PrivateRoute is redirecting us to the Login form.

If we enter some valid credentials from the Users data source (email and lowercase last name) we will be logged in and redirected to the Users list page.

We send a request to the /login endpoint and if our credentials are valid, it returns us a valid JWT authentication token. You can see it in your localStorage using Chrome dev tools.

One you refresh the page the token is still there, and you can see verifyToken method was called to get information about the currently logged in user. The token passes verification via that endpoint and token verification logic behind it, and our server sends us back the the current user’s profile.

If our token is expired or removed, or we change it to something not valid like:

we will see our error message, and GraphQL API request will return non-authorized 401 HTTP Response because token verification has failed.

While we are logged in our application should work as usually, until we preform a log out, whereupon the token will be removed from localStorage, and we will be redirected to the Login page again. To logout using JWT you simply need to remove your token from the client side.

That’s how we can use GraphQL API with JWT authentication in Node JS with React + Redux on the Frontend side. I hope it will be helpful. Feel free to clone the Repository and run/test/modify it.

That’s more or less it for this tutorial. :)