fix: amend AutoLogout logic to work with any provider and correctly logout

Signed-off-by: Juan Escalada <juanescalada175@gmail.com>
This commit is contained in:
Juan Escalada
2025-10-18 21:06:38 +09:00
parent 2259e99a6e
commit 18430adf40
2 changed files with 37 additions and 21 deletions
@@ -146,14 +146,8 @@ const ConditionalAutoLogout = ({
onPrompt: promptBeforeIdle ? onPrompt : undefined,
promptBeforeIdle: promptBeforeIdle ? promptBeforeIdleMillis : undefined,
syncTimers: 1000,
leaderElection: true,
});
// Check if the current tab is the only active tab. In practice, this gets checked once per second
if (timer.isLeader()) {
lastSeenOnlineStore.save(new Date());
}
return (
<>
{promptBeforeIdle && (
@@ -237,18 +231,35 @@ const parseConfig = (
export const AutoLogout = (props: AutoLogoutProps): JSX.Element | null => {
const identityApi = useApi(identityApiRef);
const configApi = useApi(configApiRef);
const [isLogged, setIsLogged] = useState(false);
const [isLogged, setIsLogged] = useState<boolean | null>(null);
const lastSeenOnlineStore: TimestampStore = useMemo(
() => new DefaultTimestampStore(LAST_SEEN_ONLINE_STORAGE_KEY),
[],
);
useEffect(() => {
// if the user is not logged in, the autologout feature won't affect the app even if enabled
async function isLoggedIn(identity: IdentityApi) {
if ((await identity.getCredentials()).token) {
setIsLogged(true);
} else {
try {
// Add timeout if identity.getCredentials() hangs/takes too long to return
setTimeout(() => {
if (isLogged === null || isLogged === false) {
lastSeenOnlineStore.delete();
}
}, 3000);
const creds = await identity.getCredentials();
if (creds?.token) {
setIsLogged(true);
} else {
setIsLogged(false);
lastSeenOnlineStore.delete();
}
} catch (err) {
setIsLogged(false);
lastSeenOnlineStore.delete();
}
}
isLoggedIn(identityApi);
}, [identityApi]);
}, [isLogged, lastSeenOnlineStore, identityApi]);
const {
enabled,
@@ -280,10 +291,6 @@ export const AutoLogout = (props: AutoLogoutProps): JSX.Element | null => {
}
}, [idleTimeoutMinutes, promptBeforeIdleSeconds]);
const lastSeenOnlineStore: TimestampStore = useMemo(
() => new DefaultTimestampStore(LAST_SEEN_ONLINE_STORAGE_KEY),
[],
);
const [promptOpen, setPromptOpen] = useState<boolean>(false);
const [remainingTimeCountdown, setRemainingTimeCountdown] =
@@ -291,7 +298,8 @@ export const AutoLogout = (props: AutoLogoutProps): JSX.Element | null => {
useLogoutDisconnectedUserEffect({
enableEffect: logoutIfDisconnected,
autologoutIsEnabled: enabled && isLogged,
autologoutIsEnabled: enabled,
isLoggedIn: isLogged,
idleTimeoutSeconds: idleTimeoutMinutes * 60,
lastSeenOnlineStore,
identityApi,
@@ -24,6 +24,7 @@ export const LAST_SEEN_ONLINE_STORAGE_KEY =
export type UseLogoutDisconnectedUserEffectProps = {
enableEffect: boolean;
autologoutIsEnabled: boolean;
isLoggedIn: boolean | null;
idleTimeoutSeconds: number;
lastSeenOnlineStore: TimestampStore;
identityApi: IdentityApi;
@@ -32,6 +33,7 @@ export type UseLogoutDisconnectedUserEffectProps = {
export const useLogoutDisconnectedUserEffect = ({
enableEffect,
autologoutIsEnabled,
isLoggedIn,
idleTimeoutSeconds,
lastSeenOnlineStore,
identityApi,
@@ -41,7 +43,12 @@ export const useLogoutDisconnectedUserEffect = ({
* Considers disconnected users as inactive users.
* If all Backstage tabs are closed and idleTimeoutMinutes are passed then logout the user anyway.
*/
if (autologoutIsEnabled && enableEffect) {
// Prevent lastSeen getting deleted before logged state is checked
if (isLoggedIn === null) {
return;
}
if (autologoutIsEnabled && isLoggedIn && enableEffect) {
const lastSeenOnline = lastSeenOnlineStore.get();
if (lastSeenOnline) {
const now = new Date();
@@ -51,15 +58,16 @@ export const useLogoutDisconnectedUserEffect = ({
);
if (nowSeconds - lastSeenOnlineSeconds > idleTimeoutSeconds) {
identityApi.signOut();
// Delete lastSeen time to prevent getting locked out after logout
lastSeenOnlineStore.delete();
return;
}
}
lastSeenOnlineStore.save(new Date());
} else {
lastSeenOnlineStore.delete();
}
}, [
autologoutIsEnabled,
enableEffect,
isLoggedIn,
identityApi,
idleTimeoutSeconds,
lastSeenOnlineStore,