Skip to content

Commit

Permalink
Merge pull request #1024 from nickgros/SWC-6876
Browse files Browse the repository at this point in the history
  • Loading branch information
nickgros authored Jun 20, 2024
2 parents 6ec0e7a + f816cbf commit 111eed3
Show file tree
Hide file tree
Showing 38 changed files with 836 additions and 295 deletions.
3 changes: 3 additions & 0 deletions apps/SageAccountWeb/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"devDependencies": {
"@sage-bionetworks/synapse-types": "workspace:*",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.5.2",
"@types/katex": "^0.5.0",
Expand All @@ -41,12 +42,14 @@
"@types/react-tooltip": "^4.2.4",
"@vitest/coverage-v8": "^0.34.6",
"@vitest/ui": "^0.34.7",
"@vitest/utils": "^0.34.7",
"assert": "^2.1.0",
"buffer": "^6.0.3",
"https-browserify": "^1.0.0",
"identity-obj-proxy": "^3.0.0",
"jsdom": "^21.1.2",
"memfs": "^3.5.3",
"msw": "^2.3.1",
"path-browserify": "^1.0.1",
"pluralize": "^8.0.0",
"prettier": "^2.8.8",
Expand Down
281 changes: 110 additions & 171 deletions apps/SageAccountWeb/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { StyledEngineProvider } from '@mui/material/styles'
import { AppContextConsumer } from './AppContext'
import { SageResourcesPage } from './components/SageResourcesPage'
import { AccountCreatedPage } from './components/AccountCreatedPage'
Expand All @@ -12,200 +11,140 @@ import { RegisterAccount2 } from './components/RegisterAccount2'
import { ResetPassword } from './components/ResetPassword'
import { TermsOfUsePage } from './components/TermsOfUsePage'
import React from 'react'
import { Route, Switch, useHistory } from 'react-router-dom'
import { Route, Switch } from 'react-router-dom'
import {
ApplicationSessionManager,
CookiesNotification,
SynapseClient,
SynapseContextConsumer,
SynapseContextType,
SynapseToastContainer,
} from 'synapse-react-client'
import { getSearchParam } from './URLUtils'
import './App.scss'
import AppInitializer from './AppInitializer'
import LoginPage from './LoginPage'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import TwoFactorAuthEnrollmentPage from './components/TwoFactorAuth/TwoFactorAuthEnrollmentPage'
import TwoFactorAuthBackupCodesPage from './components/TwoFactorAuth/TwoFactorAuthBackupCodesPage'
import { PersonalAccessTokensPage } from './components/PersonalAccessTokensPage'
import { OAuthClientManagementPage } from './components/OAuthClientManagementPage'
import { SourceAppProvider } from './components/useSourceApp'
import { ResetTwoFactorAuth } from './components/ResetTwoFactorAuth'
import { RESET_2FA_ROUTE, RESET_2FA_SIGNED_TOKEN_PARAM } from './Constants'
import { ResetTwoFactorAuth } from './components/TwoFactorAuth/ResetTwoFactorAuth'
import { RESET_2FA_ROUTE } from './Constants'
import { ChangePasswordPage } from './components/ChangePasswordPage'

const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 50 * 1000, // 50s
retry: false, // SynapseClient knows which queries to retry
},
},
})

function App() {
const history = useHistory()
return (
<div className="App">
<StyledEngineProvider injectFirst>
<QueryClientProvider client={queryClient}>
<ApplicationSessionManager
onTwoFactorAuthResetThroughSSO={(twoFaError, twoFaResetCode) => {
// The user completed SSO with a twoFaResetCode
// Send them to the reset 2FA page with the token
history.push(
`${RESET_2FA_ROUTE}?${RESET_2FA_SIGNED_TOKEN_PARAM}=${twoFaResetCode}`,
)
}}
>
<SourceAppProvider>
<SynapseToastContainer />
<AppInitializer>
<CookiesNotification />
<Switch>
<Route exact path="/">
<SynapseContextConsumer>
{(ctx?: SynapseContextType) => {
if (!ctx?.accessToken) {
return <LoginPage returnToUrl={'/'} />
} else {
return (
<AppContextConsumer>
{appContext => {
const isCodeSearchParam =
getSearchParam('code') !== undefined
const isProviderSearchParam =
getSearchParam('provider') !== undefined
const isInSSOFlow =
isCodeSearchParam && isProviderSearchParam
return (
<>
{appContext?.redirectURL &&
!isInSSOFlow &&
window.location.replace(
appContext?.redirectURL,
)}
</>
)
}}
</AppContextConsumer>
)
}
}}
</SynapseContextConsumer>
</Route>
<Route
exact
path="/logout"
render={props => {
SynapseClient.signOut().then(() => {
window.history.replaceState(
null,
'',
'/authenticated/myaccount',
)
})
return <></>
}}
/>
<Route exact path="/register1" component={RegisterAccount1} />
<Route exact path="/register2" component={RegisterAccount2} />
<Route exact path="/jointeam" component={JoinTeamPage} />
<Route
exact
path="/changePassword"
component={ChangePasswordPage}
/>
<Route
exact
path="/sageresources"
component={SageResourcesPage}
/>
<Route exact path="/resetPassword">
<ResetPassword returnToUrl="/authenticated/myaccount" />
</Route>
<Route exact path={RESET_2FA_ROUTE}>
<ResetTwoFactorAuth />
</Route>
<SynapseContextConsumer>
{(ctx?: SynapseContextType) => {
const isAuthenticated = !!ctx?.accessToken
<CookiesNotification />
<Switch>
<Route exact path="/">
<SynapseContextConsumer>
{(ctx?: SynapseContextType) => {
if (!ctx?.accessToken) {
return <LoginPage returnToUrl={'/'} />
} else {
return (
<AppContextConsumer>
{appContext => {
const isCodeSearchParam =
getSearchParam('code') !== undefined
const isProviderSearchParam =
getSearchParam('provider') !== undefined
const isInSSOFlow =
isCodeSearchParam && isProviderSearchParam
return (
<>
{/* If not signed in and in the "/authenticated" path, show the login page */}
{!isAuthenticated && (
<Route path="/authenticated" exact={false}>
<LoginPage />
</Route>
)}
{isAuthenticated && (
<>
<Route path={'/authenticated/validate'} exact>
<ProfileValidation />
</Route>
<Route
path={'/authenticated/signTermsOfUse'}
exact
>
<TermsOfUsePage />
</Route>
<Route path={'/authenticated/myaccount'} exact>
<AccountSettings />
</Route>
<Route
path={'/authenticated/currentaffiliation'}
exact
>
<CurrentAffiliationPage />
</Route>
<Route
path={'/authenticated/accountcreated'}
exact
>
<AccountCreatedPage />
</Route>
<Route
path={'/authenticated/certificationquiz'}
exact
>
<CertificationQuiz />
</Route>
<Route
path={'/authenticated/2fa/enroll'}
exact
render={() => <TwoFactorAuthEnrollmentPage />}
/>
<Route
path={'/authenticated/2fa/generatecodes'}
exact
render={() => <TwoFactorAuthBackupCodesPage />}
/>
<Route
path={'/authenticated/personalaccesstokens'}
exact
render={() => <PersonalAccessTokensPage />}
/>
<Route
path={'/authenticated/oauthclientmanagement'}
exact
render={() => <OAuthClientManagementPage />}
/>
</>
)}
{appContext?.redirectURL &&
!isInSSOFlow &&
window.location.replace(appContext?.redirectURL)}
</>
)
}}
</SynapseContextConsumer>
<Route exact={true} path="/login">
<LoginPage returnToUrl={'/'} />
</AppContextConsumer>
)
}
}}
</SynapseContextConsumer>
</Route>
<Route
exact
path="/logout"
render={props => {
SynapseClient.signOut().then(() => {
window.history.replaceState(null, '', '/authenticated/myaccount')
})
return <></>
}}
/>
<Route exact path="/register1" component={RegisterAccount1} />
<Route exact path="/register2" component={RegisterAccount2} />
<Route exact path="/jointeam" component={JoinTeamPage} />
<Route exact path="/changePassword" component={ChangePasswordPage} />
<Route exact path="/sageresources" component={SageResourcesPage} />
<Route exact path="/resetPassword">
<ResetPassword returnToUrl="/authenticated/myaccount" />
</Route>
<Route exact path={RESET_2FA_ROUTE}>
<ResetTwoFactorAuth />
</Route>
<SynapseContextConsumer>
{(ctx?: SynapseContextType) => {
const isAuthenticated = !!ctx?.accessToken
return (
<>
{/* If not signed in and in the "/authenticated" path, show the login page */}
{!isAuthenticated && (
<Route path="/authenticated" exact={false}>
<LoginPage />
</Route>
</Switch>
</AppInitializer>
</SourceAppProvider>
</ApplicationSessionManager>
</QueryClientProvider>
</StyledEngineProvider>
)}
{isAuthenticated && (
<>
<Route path={'/authenticated/validate'} exact>
<ProfileValidation />
</Route>
<Route path={'/authenticated/signTermsOfUse'} exact>
<TermsOfUsePage />
</Route>
<Route path={'/authenticated/myaccount'} exact>
<AccountSettings />
</Route>
<Route path={'/authenticated/currentaffiliation'} exact>
<CurrentAffiliationPage />
</Route>
<Route path={'/authenticated/accountcreated'} exact>
<AccountCreatedPage />
</Route>
<Route path={'/authenticated/certificationquiz'} exact>
<CertificationQuiz />
</Route>
<Route
path={'/authenticated/2fa/enroll'}
exact
render={() => <TwoFactorAuthEnrollmentPage />}
/>
<Route
path={'/authenticated/2fa/generatecodes'}
exact
render={() => <TwoFactorAuthBackupCodesPage />}
/>
<Route
path={'/authenticated/personalaccesstokens'}
exact
render={() => <PersonalAccessTokensPage />}
/>
<Route
path={'/authenticated/oauthclientmanagement'}
exact
render={() => <OAuthClientManagementPage />}
/>
</>
)}
</>
)
}}
</SynapseContextConsumer>
<Route exact={true} path="/login">
<LoginPage returnToUrl={'/'} />
</Route>
</Switch>
</div>
)
}
Expand Down
51 changes: 51 additions & 0 deletions apps/SageAccountWeb/src/AppWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react'
import { RESET_2FA_ROUTE, RESET_2FA_SIGNED_TOKEN_PARAM } from './Constants'
import { StyledEngineProvider } from '@mui/material/styles'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import {
ApplicationSessionManager,
SynapseToastContainer,
} from 'synapse-react-client'
import { SourceAppProvider } from './components/useSourceApp'
import AppInitializer from './AppInitializer'
import { useHistory } from 'react-router-dom'

const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 50 * 1000, // 50s
retry: false, // SynapseClient knows which queries to retry
},
},
})

/**
* Wraps the application in all required providers, and is also useful for wrapping components under test.
* @constructor
*/
export default function AppWrapper(
props: React.PropsWithChildren<Record<never, never>>,
) {
const history = useHistory()

return (
<StyledEngineProvider injectFirst>
<QueryClientProvider client={queryClient}>
<ApplicationSessionManager
onTwoFactorAuthResetThroughSSO={(twoFaError, twoFaResetCode) => {
// The user completed SSO with a twoFaResetCode
// Send them to the reset 2FA page with the token
history.push(
`${RESET_2FA_ROUTE}?${RESET_2FA_SIGNED_TOKEN_PARAM}=${twoFaResetCode}`,
)
}}
>
<SourceAppProvider>
<SynapseToastContainer />
<AppInitializer>{props.children}</AppInitializer>
</SourceAppProvider>
</ApplicationSessionManager>
</QueryClientProvider>
</StyledEngineProvider>
)
}
Loading

0 comments on commit 111eed3

Please sign in to comment.