I'm developing a React application that has user authentication with session duration. While login, the response will give session duration with userObject. I'm persisting the loggedUser object using redux-persist in localStorage. I used react-idle-timer to detect the tab idle with sessionDuration I'm getting in loggedUser Object. when user idle reached timeout then, I'll clear the localStorage results user logout.
The flow is working exactly for the single tab. when user uses multiple tab. For example, I'm opening a page in the second tab, loggedUser data will be shared using localStorage, here I'm actively using the page. but the first tab gets idle and when the duration exceeds the first tab will be loggedOut by clearing the localStorage, the user who uses second tab actively will be forced to logout, since there is no loggedUser data (localStorage).
How can I handle this?
I'm developing a React application that has user authentication with session duration. While login, the response will give session duration with userObject. I'm persisting the loggedUser object using redux-persist in localStorage. I used react-idle-timer to detect the tab idle with sessionDuration I'm getting in loggedUser Object. when user idle reached timeout then, I'll clear the localStorage results user logout.
The flow is working exactly for the single tab. when user uses multiple tab. For example, I'm opening a page in the second tab, loggedUser data will be shared using localStorage, here I'm actively using the page. but the first tab gets idle and when the duration exceeds the first tab will be loggedOut by clearing the localStorage, the user who uses second tab actively will be forced to logout, since there is no loggedUser data (localStorage).
How can I handle this?
Yeah... At the beginning I was facing the same issue. You're not alone, dear. Just add the crossTab
property to the code and your problem will be solved.
<div>
<IdleTimer
crossTab={true}
ref={idleTimerRef}
timeout={1000 * 60 * sessionTimeoutPeriod}
onIdle={onIdle}></IdleTimer>
</div>
You can set the crossTab property in the IdleTimer ponent to true.
<IdleTimer
crossTab={true}
ref={idleLogoutRef}
timeout={timeOutLogin * 60 * 1000}
onIdle={logoutUser}
onActive={stayActive}
></IdleTimer>
If not using a library, you can subscribe to on change events in local storage and refresh the page.
Example:
window.addEventListener( 'storage', function ( event ) {
console.log( event.key );
if ( event.key === 'user_logout' ) {
window.location.reload();
}
} );
The event does not fire on the tab that is making the change, but it fires on the rest of the domain's tabs in the browser.
You can make use of isLastActiveTab
callback func of the API, so that the onIdle
Callback will be executed only if the last active tab is going Idle:
//useIdleTimeout.js
const useIdleTimeout = ({promptBeforeIdle, idleTime = 1}) => {
const idleTimeout = 1000 * idleTime;
const dispatch = useDispatch();
const {isLoggedIn} = useSelector(state => state.login);
const handleIdle = () => {
if(isLoggedIn && idleTimer.isLastActiveTab()) {
// Logout Logic goes here
axios.post('/logout').then((res) => dispatch(logout())).catch(err => console.error(err));
}
}
const handleTabFocus = () => {
idleTimer.reset();
}
useEffect(() => {
window.addEventListener('focus', handleTabFocus);
return() => {
window.removeEventListener('focus', handleTabFocus);
}
},[]);
const idleTimer = useIdleTimer({
crossTab: true,
timeout: idleTimeout,
promptBeforeIdle: idleTimeout / 2,
onPrompot: onIdle,
onIdle: handleIdle,
debounce: 500
});
return {
idleTimer
}
}
export default useIdleTimeout;
In the example above i have assumed that you are storing 'isLoggedIn' variable in your redux store, to make sure that the user is logged in before performing the logout.
In your HomePage or App Component :
const App = () => {
const handleBeforeIdle = () => {
alert('Notify User, he is about to be Idle');
}
const {idleTimer} = useIdleTimeout(promptBeforeIdle: handleBeforeIdle, idleTime: 10});
return (
// ...
)
}
Note that this solution resolves two issues with cross Tabs Idle timeout: 1- Prevent the onIdle() callback function if the user is active on at least one tab, and inactive on the others. 2- onIdle() will be executed only once, by the last active tab.
I set crossTab as 'true' but it did not work until I added 'syncTimers' property
useIdleTimer({
onIdle,
timeout: 1 * 60 * 1000,
crossTab: true,
throttle: 1000,
syncTimers: 200});