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
// 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

View File

@ -52,6 +52,7 @@ class AuthContainer extends React.Component {
info: PropTypes.object,
auth: PropTypes.object,
// Props coming from withAuth
userManager: PropTypes.object,
signIn: PropTypes.func,
signOut: PropTypes.func,
}
@ -61,17 +62,55 @@ class AuthContainer extends React.Component {
this.state = {
isModalOpen: false,
showZuulClientConfig: false,
isSessionExpiredModalOpen: false,
}
this.handleModalToggle = () => {
this.setState(({ isModalOpen }) => ({
isModalOpen: !isModalOpen
}))
}
this.handleSessionExpiredModalToggle = () => {
this.setState(({ isSessionExpiredModalOpen }) => ({
isSessionExpiredModalOpen: !isSessionExpiredModalOpen
}))
}
this.handleConfigToggle = () => {
this.setState(({ 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() {
@ -96,6 +135,30 @@ class AuthContainer extends React.Component {
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() {
const { user, tenant, timezone } = this.props
const { isModalOpen, showZuulClientConfig } = this.state
@ -164,11 +227,7 @@ class AuthContainer extends React.Component {
<Button
key="SignIn"
variant={ButtonVariant.plain}
onClick={() => {
const redirect_target = window.location.href.slice(getHomepageUrl().length)
localStorage.setItem('zuul_auth_redirect', redirect_target)
this.props.signIn()
}}>
onClick={() => { this.clickOnSignIn() }}>
Sign in &nbsp;
<SignInAltIcon title='Sign In' />
</Button>
@ -185,6 +244,7 @@ class AuthContainer extends React.Component {
<UserIcon title='User details' />
&nbsp;{user.data.profile.preferred_username}&nbsp;
</Button>
{this.renderCredentialsExpiredModal()}
</div>
)
}