X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/211054b9494b034611467321203618d8fbdf05e2..07086d1ce2f74956fd3a498f184fcc0274f3e38d:/src/store/auth/auth-action.ts diff --git a/src/store/auth/auth-action.ts b/src/store/auth/auth-action.ts index fb94746ff9..8c44aec448 100644 --- a/src/store/auth/auth-action.ts +++ b/src/store/auth/auth-action.ts @@ -22,8 +22,9 @@ export const authActions = unionize({ LOGIN: {}, LOGOUT: ofType<{ deleteLinkData: boolean }>(), SET_CONFIG: ofType<{ config: Config }>(), - SET_EXTRA_TOKEN: ofType<{ extraToken: string }>(), - INIT_USER: ofType<{ user: User, token: string }>(), + SET_EXTRA_TOKEN: ofType<{ extraApiToken: string, extraApiTokenExpiration?: Date }>(), + RESET_EXTRA_TOKEN: {}, + INIT_USER: ofType<{ user: User, token: string, tokenExpiration?: Date }>(), USER_DETAILS_REQUEST: {}, USER_DETAILS_SUCCESS: ofType(), SET_SSH_KEYS: ofType(), @@ -37,21 +38,18 @@ export const authActions = unionize({ REMOTE_CLUSTER_CONFIG: ofType<{ config: Config }>(), }); -export const initAuth = (config: Config) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { +export const initAuth = (config: Config) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise => { // Cancel any link account ops in progress unless the user has // just logged in or there has been a successful link operation const data = services.linkAccountService.getLinkOpStatus(); if (!matchTokenRoute(location.pathname) && (!matchFedTokenRoute(location.pathname)) && data === undefined) { - dispatch(cancelLinking()).then(() => { - dispatch(init(config)); - }); - } else { - dispatch(init(config)); + await dispatch(cancelLinking()); } + return dispatch(init(config)); }; -const init = (config: Config) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { +const init = (config: Config) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { const remoteHosts = () => getState().auth.remoteHosts; const token = services.authService.getApiToken(); let homeCluster = services.authService.getHomeCluster(); @@ -69,11 +67,12 @@ const init = (config: Config) => (dispatch: Dispatch, getState: () => RootState, if (token && token !== "undefined") { dispatch(progressIndicatorActions.START_WORKING(WORKBENCH_LOADING_SCREEN)); - dispatch(saveApiToken(token)).then(() => { - dispatch(progressIndicatorActions.STOP_WORKING(WORKBENCH_LOADING_SCREEN)); - }).catch(() => { + try { + await dispatch(saveApiToken(token)); // .then(() => { + await dispatch(progressIndicatorActions.STOP_WORKING(WORKBENCH_LOADING_SCREEN)); + } catch (e) { dispatch(progressIndicatorActions.STOP_WORKING(WORKBENCH_LOADING_SCREEN)); - }); + } } }; @@ -82,22 +81,40 @@ export const getConfig = (dispatch: Dispatch, getState: () => RootState, service return state.remoteHostsConfig[state.localCluster]; }; -export const saveApiToken = (token: string) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise => { +export const saveApiToken = (token: string) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise => { const config = dispatch(getConfig); const svc = createServices(config, { progressFn: () => { }, errorFn: () => { } }); setAuthorizationHeader(svc, token); - return svc.authService.getUserDetails().then((user: User) => { - dispatch(authActions.INIT_USER({ user, token })); - // Upon user init, request an extra token that won't be expired on logout - // for other uses like the "get token" dialog, or S3 URL building. - dispatch(getNewExtraToken()); - }).catch(() => { + try { + const user = await svc.authService.getUserDetails(); + const client = await svc.apiClientAuthorizationService.get('current'); + const tokenExpiration = client.expiresAt ? new Date(client.expiresAt) : undefined; + dispatch(authActions.INIT_USER({ user, token, tokenExpiration })); + } catch (e) { dispatch(authActions.LOGOUT({ deleteLinkData: false })); - }); + } }; -export const getNewExtraToken = () => +export const getNewExtraToken = (reuseStored: boolean = false) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + const extraToken = getState().auth.extraApiToken; + if (reuseStored && extraToken !== undefined) { + const config = dispatch(getConfig); + const svc = createServices(config, { progressFn: () => { }, errorFn: () => { } }); + setAuthorizationHeader(svc, extraToken); + try { + // Check the extra token's validity before using it. Refresh its + // expiration date just in case it changed. + const client = await svc.apiClientAuthorizationService.get('current'); + dispatch(authActions.SET_EXTRA_TOKEN({ + extraApiToken: extraToken, + extraApiTokenExpiration: client.expiresAt ? new Date(client.expiresAt): undefined, + })); + return extraToken; + } catch (e) { + dispatch(authActions.RESET_EXTRA_TOKEN()); + } + } const user = getState().auth.user; const loginCluster = getState().auth.config.clusterConfig.Login.LoginCluster; if (user === undefined) { return; } @@ -107,7 +124,10 @@ export const getNewExtraToken = () => // allow token creation and there's no way to know that from workbench2 side in advance. const client = await services.apiClientAuthorizationService.create(undefined, false); const newExtraToken = getTokenV2(client); - dispatch(authActions.SET_EXTRA_TOKEN({ extraToken: newExtraToken })); + dispatch(authActions.SET_EXTRA_TOKEN({ + extraApiToken: newExtraToken, + extraApiTokenExpiration: client.expiresAt ? new Date(client.expiresAt): undefined, + })); return newExtraToken; } catch { console.warn("Cannot create new tokens with the current token, probably because of cluster's security settings.");