Skip to content

Commit

Permalink
Merge pull request Sage-Bionetworks#1310 from nickgros/redirect-when-…
Browse files Browse the repository at this point in the history
…not-accept-tou
  • Loading branch information
nickgros authored Oct 23, 2024
2 parents 425f6c4 + 441f0bc commit 3c001e5
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 93 deletions.
93 changes: 50 additions & 43 deletions apps/SageAccountWeb/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,65 @@
import { AppContextConsumer } from './AppContext'
import Footer from './components/Footer'
import { SageResourcesPage } from './components/SageResourcesPage'
import React, { useEffect } from 'react'
import { Route, Switch } from 'react-router-dom'
import {
CookiesNotification,
processRedirectURLInOneSage,
SynapseClient,
SynapseContextConsumer,
SynapseContextType,
useSynapseContext,
} from 'synapse-react-client'
import { useAppContext } from './AppContext'
import { AccountCreatedPage } from './components/AccountCreatedPage'
import { AccountSettings } from './components/AccountSettings'
import { CertificationQuiz } from './components/CertificationQuiz'
import { ChangePasswordPage } from './components/ChangePasswordPage'
import { CurrentAffiliationPage } from './components/CurrentAffiliationPage'
import Footer from './components/Footer'
import { JoinTeamPage } from './components/JoinTeamPage'
import { OAuthClientManagementPage } from './components/OAuthClientManagementPage'
import { PersonalAccessTokensPage } from './components/PersonalAccessTokensPage'
import { ProfileValidation } from './components/ProfileValidation/ProfileValidation'
import { RegisterAccount1 } from './components/RegisterAccount1'
import { RegisterAccount2 } from './components/RegisterAccount2'
import { ResetPassword } from './components/ResetPassword'
import { SageResourcesPage } from './components/SageResourcesPage'
import { SignUpdatedTermsOfUsePage } from './components/SignUpdatedTermsOfUsePage'
import { TermsOfUsePage } from './components/TermsOfUsePage'
import React from 'react'
import { Route, Switch } from 'react-router-dom'
import {
CookiesNotification,
SynapseClient,
SynapseContextConsumer,
SynapseContextType,
processRedirectURLInOneSage,
} from 'synapse-react-client'
import { ResetTwoFactorAuth } from './components/TwoFactorAuth/ResetTwoFactorAuth'
import TwoFactorAuthBackupCodesPage from './components/TwoFactorAuth/TwoFactorAuthBackupCodesPage'
import TwoFactorAuthEnrollmentPage from './components/TwoFactorAuth/TwoFactorAuthEnrollmentPage'
import { WebhookManagementPage } from './components/WebhooksManagementPage'
import { RESET_2FA_ROUTE } from './Constants'
import useMaybeRedirectToSignTermsOfService from './hooks/useMaybeRedirectToSignTermsOfService'
import LoginPage from './LoginPage'
import { getSearchParam } from './URLUtils'
import './App.scss'
import LoginPage from './LoginPage'
import TwoFactorAuthEnrollmentPage from './components/TwoFactorAuth/TwoFactorAuthEnrollmentPage'
import TwoFactorAuthBackupCodesPage from './components/TwoFactorAuth/TwoFactorAuthBackupCodesPage'
import { PersonalAccessTokensPage } from './components/PersonalAccessTokensPage'
import { OAuthClientManagementPage } from './components/OAuthClientManagementPage'
import { ResetTwoFactorAuth } from './components/TwoFactorAuth/ResetTwoFactorAuth'
import { RESET_2FA_ROUTE } from './Constants'
import { ChangePasswordPage } from './components/ChangePasswordPage'
import { SignUpdatedTermsOfUsePage } from './components/SignUpdatedTermsOfUsePage'

function LoggedInRedirector() {
const { accessToken } = useSynapseContext()
const appContext = useAppContext()

const isCodeSearchParam = getSearchParam('code') !== undefined
const isProviderSearchParam = getSearchParam('provider') !== undefined
const isInSSOFlow = isCodeSearchParam && isProviderSearchParam

const { mayRedirect: mayRedirectToSignToS } =
useMaybeRedirectToSignTermsOfService()

useEffect(() => {
// User is on the root page (implied by route), logged in, not in the SSO Flow, and does not need to sign the ToS
// then redirect!
if (accessToken && !isInSSOFlow && !mayRedirectToSignToS) {
// take user back to page they came from in the source app, if stored in a cookie
const isProcessed = processRedirectURLInOneSage()
if (!isProcessed && appContext?.redirectURL) {
// if not in the cookie, take them to
window.location.replace(appContext?.redirectURL)
}
}
}, [accessToken, appContext?.redirectURL, isInSSOFlow, mayRedirectToSignToS])
return <></>
}

function App() {
return (
Expand All @@ -44,28 +72,7 @@ function App() {
if (!ctx?.accessToken) {
return <LoginPage returnToUrl={'/'} />
} else {
return (
<AppContextConsumer>
{appContext => {
// User is logged in and visiting the root, redirect (unless in the SSO Flow)
const isCodeSearchParam =
getSearchParam('code') !== undefined
const isProviderSearchParam =
getSearchParam('provider') !== undefined
const isInSSOFlow =
isCodeSearchParam && isProviderSearchParam
if (!isInSSOFlow) {
// take user back to page they came from in the source app, if stored in a cookie
const isProcessed = processRedirectURLInOneSage()
if (!isProcessed && appContext?.redirectURL) {
// if not in the cookie, take them to
window.location.replace(appContext?.redirectURL)
}
}
return <></>
}}
</AppContextConsumer>
)
return <LoggedInRedirector />
}
}}
</SynapseContextConsumer>
Expand Down
47 changes: 8 additions & 39 deletions apps/SageAccountWeb/src/AppInitializer.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
import { SignedTokenInterface } from '@sage-bionetworks/synapse-types'
import React, { useEffect, useState } from 'react'
import { Redirect } from 'react-router-dom'
import { getSearchParam } from './URLUtils'
import {
SignedTokenInterface,
TermsOfServiceState,
} from '@sage-bionetworks/synapse-types'
import {
storeLastPlace,
SynapseConstants,
SynapseUtilityFunctions,
useApplicationSessionContext,
useFramebuster,
SynapseConstants,
} from 'synapse-react-client'
import { AppContextProvider } from './AppContext'
import { useSourceApp } from './components/useSourceApp'
import useMaybeRedirectToSignTermsOfService from './hooks/useMaybeRedirectToSignTermsOfService'
import { getSearchParam } from './URLUtils'

function AppInitializer(props: { children?: React.ReactNode }) {
const [signedToken, setSignedToken] = useState<
SignedTokenInterface | undefined
>()
const [skippedSigningUpdatedToS, setSkippedSigningUpdatedToS] = useState<
boolean | undefined
>()

const isFramed = useFramebuster()
const { appId, appURL } = useSourceApp()

Expand Down Expand Up @@ -65,32 +58,11 @@ function AppInitializer(props: { children?: React.ReactNode }) {
) as SignedTokenInterface
setSignedToken(localStorageParamToken)
}

// SignUpdatedTermsOfUsePage sets this session storage value if the user decided to skip
const sessionStorageSkippedToS = sessionStorage.getItem('skippedSigningToS')
setSkippedSigningUpdatedToS(sessionStorageSkippedToS === 'true')
}, [])

// Detect if terms of service are up to date. If not, route to either the Pledge or a page where the user can sign the updated terms.
// Note, if the status is "MUST_AGREE_SOON", then the new page will offer a "Skip" button
const { termsOfServiceStatus } = useApplicationSessionContext()
let redirectRoute = undefined
if (termsOfServiceStatus) {
if (
termsOfServiceStatus.userCurrentTermsOfServiceState !=
TermsOfServiceState.UP_TO_DATE &&
skippedSigningUpdatedToS === false
) {
if (termsOfServiceStatus.lastAgreementDate == undefined) {
redirectRoute = '/authenticated/signTermsOfUse'
} else {
if (location.pathname != '/authenticated/signUpdatedTermsOfUse') {
redirectRoute = '/authenticated/signUpdatedTermsOfUse'
storeLastPlace()
}
}
}
}
// Anywhere in the app, redirect the user to sign the ToS if required
useMaybeRedirectToSignTermsOfService()

return (
<AppContextProvider
appContext={{
Expand All @@ -99,9 +71,6 @@ function AppInitializer(props: { children?: React.ReactNode }) {
signedToken,
}}
>
{!!redirectRoute && location.pathname != redirectRoute && (
<Redirect to={redirectRoute} />
)}
{!isFramed && props.children}
</AppContextProvider>
)
Expand Down
24 changes: 13 additions & 11 deletions apps/SageAccountWeb/src/components/SignUpdatedTermsOfUsePage.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
import React, { useState } from 'react'
import {
displayToast,
SynapseContextUtils,
SynapseQueries,
GovernanceMarkdownGithub,
restoreLastPlace,
} from 'synapse-react-client'
import {
Box,
Button,
Expand All @@ -14,8 +6,17 @@ import {
FormControlLabel,
Paper,
} from '@mui/material'
import { StyledOuterContainer } from './StyledComponents'
import { TermsOfServiceState } from '@sage-bionetworks/synapse-types'
import React, { useState } from 'react'
import { useHistory } from 'react-router-dom'
import {
displayToast,
GovernanceMarkdownGithub,
restoreLastPlace,
SynapseContextUtils,
SynapseQueries,
} from 'synapse-react-client'
import { StyledOuterContainer } from './StyledComponents'

export type SignUpdatedTermsOfUsePageProps = {}

Expand All @@ -26,6 +27,7 @@ export const SignUpdatedTermsOfUsePage = (
const [isCheckboxSelected, setIsCheckboxSelected] = useState(false)
const { accessToken } = SynapseContextUtils.useSynapseContext()
const { mutate: signTermsOfService } = SynapseQueries.useSignTermsOfService()
const history = useHistory()

const { data: tosInfo } = SynapseQueries.useTermsOfServiceInfo()
const { data: tosStatus } = SynapseQueries.useTermsOfServiceStatus(
Expand All @@ -50,7 +52,7 @@ export const SignUpdatedTermsOfUsePage = (
},
{
onSuccess: () => {
restoreLastPlace()
restoreLastPlace(history)
},
onError: err => {
displayToast(err.reason as string, 'danger')
Expand Down Expand Up @@ -105,7 +107,7 @@ export const SignUpdatedTermsOfUsePage = (
sx={{ width: '100%' }}
onClick={() => {
sessionStorage.setItem('skippedSigningToS', 'true')
restoreLastPlace()
restoreLastPlace(history)
}}
disabled={isLoading}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { TermsOfServiceState } from '@sage-bionetworks/synapse-types'
import { useEffect, useRef } from 'react'
import { useHistory, useLocation } from 'react-router-dom'
import {
storeLastPlace,
useApplicationSessionContext,
} from 'synapse-react-client'

type UseMaybeRedirectToSignTermsOfServiceReturn = {
// if true, the user may be redirected (or has already been redirected) to sign the ToS
// i.e. don't assume the ToS has been signed and boot the user to the original app until this returns false!
mayRedirect: boolean
}

export default function useMaybeRedirectToSignTermsOfService(): UseMaybeRedirectToSignTermsOfServiceReturn {
// Detect if terms of service are up to date. If not, route to either the Pledge or a page where the user can sign the updated terms.
// Note, if the status is "MUST_AGREE_SOON", then the new page will offer a "Skip" button
const skippedSigningUpdatedToS =
sessionStorage.getItem('skippedSigningToS') === 'true'
const { termsOfServiceStatus } = useApplicationSessionContext()

const history = useHistory()
const location = useLocation()

// true until we confirm we will not redirect
const mayRedirect = useRef(true)

useEffect(() => {
if (termsOfServiceStatus) {
if (
termsOfServiceStatus.userCurrentTermsOfServiceState !=
TermsOfServiceState.UP_TO_DATE &&
!skippedSigningUpdatedToS
) {
if (
termsOfServiceStatus.lastAgreementDate == undefined &&
location.pathname != '/authenticated/signTermsOfUse'
) {
history.push('/authenticated/signTermsOfUse')
} else if (
termsOfServiceStatus.lastAgreementDate != null &&
location.pathname != '/authenticated/signUpdatedTermsOfUse'
) {
storeLastPlace()
history.push('/authenticated/signUpdatedTermsOfUse')
}
}
mayRedirect.current = false
}
}, [
termsOfServiceStatus,
skippedSigningUpdatedToS,
location.pathname,
history,
])

return { mayRedirect: mayRedirect.current }
}

0 comments on commit 3c001e5

Please sign in to comment.