Skip to content

Commit

Permalink
ZKUI-29 - Fix reauth modal popup behavior
Browse files Browse the repository at this point in the history
The issue:
When the user switch tab or leave the laptop for while, the reauth
modal will popup but the behavior is not clear and it confuse the
user.

We identify 2 issues:

1. A race condition
When the user go back to the tab, the UI will trigger both reauth
(because the token might be expired) and refresh the instance
(See the `useEffect` in `Routes.jsx`).
This will result a 401 and display the popup even though the token
is refresh right after.

2. A token expired for too long which require a relogin Keycloak.
In this case if the user try to click on `Reload` is will try to
reauth but, it will fail and the user will be stuck until the page
is refreshed.

To address both issues, we choose to :
- check the expired time in the polling.
This prevent to display a 401 right after the user come back from
a new tab.
- add `ADD_OIDC_USER` and `LOAD_CLIENTS_SUCCESS` to remove modal
- only log out or reload the entire page if the modal appear

Finally, this fix is a workaround before we migrate to Module
Federation and react query.
  • Loading branch information
MonPote committed Dec 7, 2021
1 parent 34fa3d7 commit b45aede
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 24 deletions.
20 changes: 12 additions & 8 deletions src/react/Routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,18 @@ function PrivateRoutes() {
// That will fix management API request being canceled during autentication.
dispatch(loadClients());

const refreshIntervalStatsUnit = setInterval(
() => dispatch(loadInstanceLatestStatus()),
10000,
);
const refreshIntervalStatsSeries = setInterval(
() => dispatch(loadInstanceStats()),
10000,
);
const refreshIntervalStatsUnit = setInterval(() => {
const currentTime = Math.floor(Date.now() / 1000);
if (user.expires_at >= currentTime) {
dispatch(loadInstanceLatestStatus());
}
}, 10000);
const refreshIntervalStatsSeries = setInterval(() => {
const currentTime = Math.floor(Date.now() / 1000);
if (user.expires_at >= currentTime) {
dispatch(loadInstanceStats());
}
}, 10000);
return () => {
clearInterval(refreshIntervalStatsUnit);
clearInterval(refreshIntervalStatsSeries);
Expand Down
1 change: 1 addition & 0 deletions src/react/ZenkoUI.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ function ZenkoUI() {
const isConfigLoaded = useSelector(
(state: AppState) => state.auth.isConfigLoaded,
);

const configFailure = useSelector(
(state: AppState) => state.auth.configFailure,
);
Expand Down
2 changes: 2 additions & 0 deletions src/react/reducers/networkActivity.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export default function networkActivity(
authFailure: true,
};
case 'NETWORK_AUTH_RESET':
case 'ADD_OIDC_USER':
case 'LOAD_CLIENTS_SUCCESS':
return {
...state,
authFailure: false,
Expand Down
18 changes: 3 additions & 15 deletions src/react/ui-elements/ReauthDialog.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// @flow
import { loadClients, networkAuthReset } from '../actions';
import { useDispatch, useSelector } from 'react-redux';
import { useSelector } from 'react-redux';
import type { AppState } from '../../types/state';
import { Button } from '@scality/core-ui/dist/next';
import { CustomModal as Modal } from './Modal';
import React from 'react';
import { push } from 'connected-react-router';
import { spacing } from '@scality/core-ui/dist/style/theme';

const DEFAULT_MESSAGE = 'We need to log you in.';
Expand All @@ -17,26 +15,16 @@ const ReauthDialog = () => {
const errorMessage = useSelector((state: AppState) =>
state.uiErrors.errorType === 'byAuth' ? state.uiErrors.errorMsg : null,
);
const pathname = useSelector(
(state: AppState) => state.router.location.pathname,
);
const oidcLogout = useSelector((state: AppState) => state.auth.oidcLogout);

const dispatch = useDispatch();

const reauth = pathName => {
dispatch(networkAuthReset());
dispatch(loadClients()).then(() => dispatch(push(pathName)));
};

if (!needReauth) {
return null;
}

return (
<Modal
id="reauth-dialog-modal"
close={() => reauth(pathname)}
close={() => window.location.reload()}
footer={
<div>
{oidcLogout && (
Expand All @@ -50,7 +38,7 @@ const ReauthDialog = () => {
)}
<Button
variant="primary"
onClick={() => reauth(pathname)}
onClick={() => window.location.reload()}
label="Reload"
/>
</div>
Expand Down
4 changes: 3 additions & 1 deletion src/types/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,9 @@ export type NetworkActivityAction =
| NetworkActivityAuthFailureAction
| NetworkActivityStartAction
| NetworkActivityEndAction
| NetworkActivityAuthResetAction;
| NetworkActivityAuthResetAction
| AddOIDCUserAction
| LoadClientsSuccessAction;

// configuration actions
export type InstanceStatusAction = {|
Expand Down

0 comments on commit b45aede

Please sign in to comment.