Refetch user session when it expires (#9632)

This commit is contained in:
Sergei Garin 2024-04-16 14:21:26 +03:00 committed by GitHub
parent 7e345075b3
commit 30a80db4b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 65 additions and 1 deletions

View File

@ -304,6 +304,8 @@ function AppRouter(props: AppRouterProps) {
}, [props, /* should never change */ navigate])
const userSession = authService?.cognito.userSession.bind(authService.cognito) ?? null
const refreshUserSession =
authService?.cognito.refreshUserSession.bind(authService.cognito) ?? null
const registerAuthEventListener = authService?.registerAuthEventListener ?? null
const initialBackend: Backend =
isAuthenticationDisabled && projectManagerUrl != null && projectManagerRootDirectory != null
@ -425,6 +427,13 @@ function AppRouter(props: AppRouterProps) {
mainPageUrl={mainPageUrl}
userSession={userSession}
registerAuthEventListener={registerAuthEventListener}
refreshUserSession={
refreshUserSession
? async () => {
await refreshUserSession()
}
: null
}
>
{result}
</SessionProvider>

View File

@ -273,6 +273,13 @@ export class Cognito {
return results.Err(cognitoUserResult.val)
}
}
/**
* Refresh the current user's session.
*/
async refreshUserSession() {
return Promise.resolve(results.Ok(null))
}
}
// ===================

View File

@ -273,6 +273,27 @@ export class Cognito {
return result.mapErr(intoAmplifyErrorOrThrow).mapErr(intoSignInWithPasswordErrorOrThrow)
}
/**
* Refresh the current user session.
*/
async refreshUserSession() {
const result = await results.Result.wrapAsync(async () => {
const currentUser = await currentAuthenticatedUser()
const refreshToken = (await amplify.Auth.currentSession()).getRefreshToken()
await new Promise((resolve, reject) => {
currentUser.unwrap().refreshSession(refreshToken, (error, session) => {
if (error instanceof Error) {
reject(error)
} else {
resolve(session)
}
})
})
})
return result.mapErr(intoCurrentSessionErrorType)
}
/** Sign out the current user. */
async signOut() {
// FIXME [NP]: https://github.com/enso-org/cloud-v2/issues/341

View File

@ -2,6 +2,8 @@
* currently authenticated user's session. */
import * as React from 'react'
import * as reactQuery from '@tanstack/react-query'
import * as asyncEffectHooks from '#/hooks/asyncEffectHooks'
import * as refreshHooks from '#/hooks/refreshHooks'
@ -44,12 +46,17 @@ export interface SessionProviderProps {
readonly mainPageUrl: URL
readonly registerAuthEventListener: listen.ListenFunction | null
readonly userSession: (() => Promise<cognito.UserSession | null>) | null
readonly refreshUserSession: (() => Promise<void>) | null
readonly children: React.ReactNode
}
const FIVE_MINUTES_MS = 300_000
const SIX_HOURS_MS = 21_600_000
/** A React provider for the session of the authenticated user. */
export default function SessionProvider(props: SessionProviderProps) {
const { mainPageUrl, children, userSession, registerAuthEventListener } = props
const { mainPageUrl, children, userSession, registerAuthEventListener, refreshUserSession } =
props
const [refresh, doRefresh] = refreshHooks.useRefresh()
const [initialized, setInitialized] = React.useState(false)
const errorCallbacks = React.useRef(new Set<(error: Error) => void>())
@ -89,6 +96,26 @@ export default function SessionProvider(props: SessionProviderProps) {
[refresh]
)
const timeUntilRefresh = session
? // If the session has not expired, we should refresh it when it is 5 minutes from expiring.
new Date(session.expireAt).getTime() - Date.now() - FIVE_MINUTES_MS
: Infinity
reactQuery.useQuery({
queryKey: ['userSession'],
queryFn: refreshUserSession
? () =>
refreshUserSession()
.then(() => {
doRefresh()
})
.then(() => null)
: reactQuery.skipToken,
refetchOnWindowFocus: true,
refetchIntervalInBackground: true,
refetchInterval: timeUntilRefresh < SIX_HOURS_MS ? timeUntilRefresh : SIX_HOURS_MS,
})
// Register an effect that will listen for authentication events. When the event occurs, we
// will refresh or clear the user's session, forcing a re-render of the page with the new
// session.