web UI: Add a credentials renew modal

Check every second for an authenticated user's token expiration.
If the token has expired, display a modal providing a link to
log in again.
Closing the modal properly logs the user out.

Change-Id: I0baa4f415bdc61735fc42f9fd80a58309976096f
This commit is contained in:
Matthieu Huin 2021-11-17 22:46:53 +01:00
parent 41d6008a3d
commit 84ab85a306
2 changed files with 65 additions and 13 deletions

View File

@ -143,14 +143,6 @@ class ZuulAuthProvider extends React.Component {
} }
}) })
// This is called when a token is expired
userManager.events.addAccessTokenExpired(() => {
console.log('Auth token expired')
userManager.removeUser()
this.props.dispatch(userLoggedOut())
this.props.channel.postMessage({ 'type': 'signOut' })
})
// This is called about 1 minute before a token is expired. We will try // This is called about 1 minute before a token is expired. We will try
// to renew the token. We use a leader election so that only one tab // to renew the token. We use a leader election so that only one tab
// makes the attempt; the others will receive the token via a broadcast // makes the attempt; the others will receive the token via a broadcast

View File

@ -52,6 +52,7 @@ class AuthContainer extends React.Component {
info: PropTypes.object, info: PropTypes.object,
auth: PropTypes.object, auth: PropTypes.object,
// Props coming from withAuth // Props coming from withAuth
userManager: PropTypes.object,
signIn: PropTypes.func, signIn: PropTypes.func,
signOut: PropTypes.func, signOut: PropTypes.func,
} }
@ -61,17 +62,55 @@ class AuthContainer extends React.Component {
this.state = { this.state = {
isModalOpen: false, isModalOpen: false,
showZuulClientConfig: false, showZuulClientConfig: false,
isSessionExpiredModalOpen: false,
} }
this.handleModalToggle = () => { this.handleModalToggle = () => {
this.setState(({ isModalOpen }) => ({ this.setState(({ isModalOpen }) => ({
isModalOpen: !isModalOpen isModalOpen: !isModalOpen
})) }))
} }
this.handleSessionExpiredModalToggle = () => {
this.setState(({ isSessionExpiredModalOpen }) => ({
isSessionExpiredModalOpen: !isSessionExpiredModalOpen
}))
}
this.handleConfigToggle = () => { this.handleConfigToggle = () => {
this.setState(({ showZuulClientConfig }) => ({ this.setState(({ showZuulClientConfig }) => ({
showZuulClientConfig: !showZuulClientConfig showZuulClientConfig: !showZuulClientConfig
})) }))
} }
this.onAccessTokenExpired = () => {
// If the token has expired, show the modal
console.debug('Token expired')
this.setState(() => ({
isSessionExpiredModalOpen: true,
isModalOpen: false,
}))
}
this.onUserLoaded = () => {
// If another tab logged in while our expired modal is shown, go
// ahead and clear it.
console.debug('User signed in')
this.setState(() => ({
isSessionExpiredModalOpen: false,
}))
}
}
clickOnSignIn() {
const redirect_target = window.location.href.slice(getHomepageUrl().length)
localStorage.setItem('zuul_auth_redirect', redirect_target)
this.props.signIn()
}
componentDidMount() {
this.props.userManager.events.addAccessTokenExpired(this.onAccessTokenExpired)
this.props.userManager.events.addUserLoaded(this.onUserLoaded)
}
componentWillUnmount() {
this.props.userManager.events.removeAccessTokenExpired(this.onAccessTokenExpired)
this.props.userManager.events.removeUserLoaded(this.onUserLoaded)
} }
componentDidUpdate() { componentDidUpdate() {
@ -96,6 +135,30 @@ class AuthContainer extends React.Component {
return ZCconfig return ZCconfig
} }
renderCredentialsExpiredModal() {
const { isSessionExpiredModalOpen } = this.state
return <React.Fragment>
<Modal
position='top'
title='You are being logged out'
isOpen={isSessionExpiredModalOpen}
variant={ModalVariant.small}
onClose={() => {
this.handleSessionExpiredModalToggle()
this.props.signOut()
}}>
Your session has expired. &nbsp;
<Button
key="SignIn"
isInline
variant={ButtonVariant.link}
onClick={() => { this.clickOnSignIn() }}>
Please renew your credentials.
</Button>
</Modal>
</React.Fragment>
}
renderModal() { renderModal() {
const { user, tenant, timezone } = this.props const { user, tenant, timezone } = this.props
const { isModalOpen, showZuulClientConfig } = this.state const { isModalOpen, showZuulClientConfig } = this.state
@ -164,11 +227,7 @@ class AuthContainer extends React.Component {
<Button <Button
key="SignIn" key="SignIn"
variant={ButtonVariant.plain} variant={ButtonVariant.plain}
onClick={() => { onClick={() => { this.clickOnSignIn() }}>
const redirect_target = window.location.href.slice(getHomepageUrl().length)
localStorage.setItem('zuul_auth_redirect', redirect_target)
this.props.signIn()
}}>
Sign in &nbsp; Sign in &nbsp;
<SignInAltIcon title='Sign In' /> <SignInAltIcon title='Sign In' />
</Button> </Button>
@ -185,6 +244,7 @@ class AuthContainer extends React.Component {
<UserIcon title='User details' /> <UserIcon title='User details' />
&nbsp;{user.data.profile.preferred_username}&nbsp; &nbsp;{user.data.profile.preferred_username}&nbsp;
</Button> </Button>
{this.renderCredentialsExpiredModal()}
</div> </div>
) )
} }