How to logout user when token expires in react app
The issue you are facing is simple. Your AuthReducer takes in the initialState only once when its created. Now when you reload your app, everything is initialized again and the expiry is taken care of by your logic. However on Route change It doesn't re-evaluate your initialState.
However what you can do is while using setContext
you can check for validation of expiry by decoding the token using jwtDecode
and refresh the token if it expired and save in localStorage since this is executed on every request
const authLink = setContext(async () => {
let token = localStorage.getItem('JWT_Token')
const { exp } = jwtDecode(token)
// Refresh the token a minute early to avoid latency issues
const expirationTime = (exp * 1000) - 60000
if (Date.now() >= expirationTime) {
token = await refreshToken()
// set LocalStorage here based on response;
}
return {
// you can set your headers directly here based on the new token/old token
headers: {
...
}
}
})
However since you wish to redirect to login page and not refresh token when the token expired you can make use of custom history object with Routes
src/history.js
import { createBrowserHistory } from 'history';
const history = createBrowserHistory()
export default history;
App.js
import history from '/path/to/history.js';
import { Router } from 'react-router-dom';
<AuthProvider>
<Router history={history}>
<div className="App wrapper">
<Routes/>
</div>
</Router>
</AuthProvider>
and then in setContext you could do
import history from '/path/to/history';
const authLink = setContext(async () => {
let token = localStorage.getItem('JWT_Token')
const { exp } = jwtDecode(token)
const expirationTime = (exp * 1000) - 60000
if (Date.now() >= expirationTime) {
localStorage.clear();
history.push('/login');
}
return {
// you can set your headers directly here based on the old token
headers: {
...
}
}
})
For your your problem the solution might be like:
- Remove the auth part from the context. (Bad practice)
- Create a component with
react-router
subscribed to check the auth state of the user. - Render it in the
main
component.
authverify.component.js
import { withRouter } from "react-router-dom";
const AuthVerifyComponent = ({ history }) => {
history.listen(() => { // <--- Here you subscribe to the route change
if (localStorage.getItem("JWT_Token")) {
const jwt_Token_decoded = Jwt_Decode(localStorage.getItem("JWT_Token"));
console.log(jwt_Token_decoded.exp * 1000);
console.log(Date.now());
if (jwt_Token_decoded.exp * 1000 < Date.now()) {
localStorage.clear();
} else {
initialstate.user = jwt_Token_decoded;
}
}
});
return <div></div>;
};
export default withRouter(AuthVerifyComponent);
app.js
<AuthProvider>
<Router>
<div className="App wrapper">
<Routes />
<AuthVerifyComponent />
</div>
</Router>
</AuthProvider>;