I am trying to verify that the user is authenticated with passport before allowing them to access a certain route. To do so, I need to access my API which will return the authentication status of my user. However, I wish to make the call before the route has rendered so that the route remains protected. Here is my current attempt:
import React, { Component } from 'react';
import { Route, Redirect } from 'react-router-dom';
class ProtectedRoute extends Component {
constructor(){
super()
this.fetchAuthState().then(result => console.log('result',result))
this.state = {
isAuth: false,
error: null
}
}
async fetchAuthState() {
try {
const response = await fetch('http://localhost:3001/logincheck', {
headers: {
'content-type': 'application/json',
accept: 'application/json',
},
});
return await response.json();
} catch (error) {
console.error(error);
this.setState({ error });
}
};
render() {
console.log('client check', this.state.isAuth)
const { component: Component, ...props } = this.props
return (
<Route
{...props}
render={props => (
this.state.isAuth ?
<Component {...props} /> :
<Redirect to='/login' />
)}
/>
)
}
}
export default ProtectedRoute;
Here is the server side code:
app.get('/logincheck', (req, res) =>{
res.send(req.isAuthenticated())
})
I figured that because of the react lifecycle, the fetch should be called in the constructor, and then stored in state. However, when I try to store the result, either directly or in a temporary variable, the fetch result shows undefined. I have checked the passport docs for any client side form of isAuthenticated(), but it appears that it only works server side. I was considering implementing a jwt token system to see if that would be a better way to maintain authentication status but thought that having an api route to check would be easier. I am new to web development so any advice/criticism would be much appreciated!
You can accomplish this by adding an extra state variable - something like loading
- and then toggling it when the API returns a response. But I would refactor your code slightly to make it look like so:
import React, { Component } from 'react';
import { Route, Redirect } from 'react-router-dom';
class ProtectedRoute extends Component {
constructor(){
super()
this.state = {
loading: true,
isAuth: false,
error: null
}
}
componentDidMount(){
fetch('http://localhost:3001/logincheck', {
headers: {
'content-type': 'application/json',
accept: 'application/json',
},
})
.then(res => res.json())
.then(json => {
this.setState({isAuth: json.isAuth, loading: false})
})
.catch(e => console.log(e))
}
render() {
console.log('client check', this.state.isAuth)
const { component: Component, ...props } = this.props;
const {loading, isAuth} = this.state;
return (
<Route
{...props}
render={() => {
return loading ?
<div>Loading...</div>
:
isAuth ?
this.props.children
:
<Redirect to='/login' />
}} />
)
}
}
export default ProtectedRoute;