diff --git a/projects/Mallard/src/authentication/authorizers/OktaAuthorizer.ts b/projects/Mallard/src/authentication/authorizers/OktaAuthorizer.ts index 603a2c9d8..f9244b7cc 100644 --- a/projects/Mallard/src/authentication/authorizers/OktaAuthorizer.ts +++ b/projects/Mallard/src/authentication/authorizers/OktaAuthorizer.ts @@ -4,12 +4,12 @@ import { isAuthenticated, } from '@okta/okta-react-native'; import { oktaDataCache } from 'src/helpers/storage'; +import { canViewEditionOkta } from '../helpers'; import { Authorizer } from '../lib/Authorizer'; import type { AuthResult } from '../lib/Result'; import { flat, InvalidResult, ValidResult } from '../lib/Result'; import { fetchMembershipDataOkta } from '../services/membership'; import { oktaAuth } from '../services/okta'; -import { canViewEditionOkta } from '../helpers'; const authWithTokens = async (mtoken: string): Promise> => { try { @@ -24,7 +24,7 @@ const authWithTokens = async (mtoken: string): Promise> => { }), ); } catch (e) { - console.log('AUTH WITH TOKENS ERROR: ', e); + return InvalidResult(); } }; @@ -34,9 +34,11 @@ export default new Authorizer({ authCaches: [], auth: async () => { // eslint-disable-next-line - const { access_token } = await oktaAuth(); - const data = await authWithTokens(access_token); - console.log('VALID RESULT?: ', data); + const authResponse = await oktaAuth(); + if (!authResponse) { + return InvalidResult(); + } + const data = await authWithTokens(authResponse.access_token); return data; }, authWithCachedCredentials: async () => { diff --git a/projects/Mallard/src/authentication/helpers.ts b/projects/Mallard/src/authentication/helpers.ts index 7b4563397..4efc630a2 100644 --- a/projects/Mallard/src/authentication/helpers.ts +++ b/projects/Mallard/src/authentication/helpers.ts @@ -33,14 +33,14 @@ const canViewEdition = (userData: IdentityAuthData): boolean => * If they have a Guardian email we want to check that they've validated their email, * otherwise we don't really mind */ -const isStaffMemberOkta = (userData: IdentityAuthData) => +const isStaffMemberOkta = (userData: any) => isGuardianEmail(userData.userDetails.preferred_username); /** * This takes the membersDataApiResponse and is responsible for returning a boolean * describing whether or not the user has the relevant permissions to use the app */ -const canViewEditionOkta = (userData: IdentityAuthData): boolean => { +const canViewEditionOkta = (userData: any): boolean => { return ( userData.membershipDetails.contentAccess.digitalPack || isStaffMemberOkta(userData) diff --git a/projects/Mallard/src/authentication/services/okta.ts b/projects/Mallard/src/authentication/services/okta.ts index fb58aca23..eccbb89e4 100644 --- a/projects/Mallard/src/authentication/services/okta.ts +++ b/projects/Mallard/src/authentication/services/okta.ts @@ -1,13 +1,10 @@ import { createConfig, - EventEmitter, - getUserFromIdToken, - introspectRefreshToken, signInWithBrowser, signOut, } from '@okta/okta-react-native'; import { Platform } from 'react-native'; -import { fromResponse } from '../lib/Result'; +import { errorService } from 'src/services/errors'; const oktaInitialisation = () => { createConfig({ @@ -30,43 +27,15 @@ const oktaInitialisation = () => { 'offline_access', ], requireHardwareBackedKeyStore: true, - }) - .then(() => { - console.log('OKTA CORRECT'); - }) - .catch(() => { - console.log('OKTA ERROR'); - }); - - EventEmitter.addListener('onError', function (e: Event) { - console.log(e); }); }; const oktaAuth = async () => { try { const attempt = await signInWithBrowser(); - // const user = await getUserFromIdToken(); - // console.log('okta user: ', user); - console.log('okta attempt:', attempt); return attempt; } catch (e) { - console.log('OKTA ERROR: '); - console.log(e); - } -}; - -const oktaAuthFromResponse = async () => { - try { - const attempt = await signInWithBrowser(); - const user = await getUserFromIdToken(); - console.log('okta user: ', user); - return fromResponse(user, { - valid: () => user, - }); - } catch (e) { - console.log('OKTA ERROR: '); - console.log(e); + errorService.captureException(e); } }; @@ -75,8 +44,8 @@ const oktaSignOut = async () => { const attempt = await signOut(); return attempt; } catch (e) { - console.log(e); + errorService.captureException(e); } }; -export { oktaInitialisation, oktaAuth, oktaAuthFromResponse, oktaSignOut }; +export { oktaInitialisation, oktaAuth, oktaSignOut }; diff --git a/projects/Mallard/src/components/Modals/SignInFailedModal.tsx b/projects/Mallard/src/components/Modals/SignInFailedModal.tsx index 84b9b75b1..5ec52ab23 100644 --- a/projects/Mallard/src/components/Modals/SignInFailedModal.tsx +++ b/projects/Mallard/src/components/Modals/SignInFailedModal.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { useOkta } from 'src/hooks/use-okta-sign-in'; import { RouteNames } from 'src/navigation/NavigationModels'; import type { MainStackParamList } from 'src/navigation/NavigationModels'; +import { remoteConfigService } from 'src/services/remote-config'; import { CenterWrapper } from '../CenterWrapper/CenterWrapper'; import { SignInFailedModalCard } from '../SignInFailedModalCard'; @@ -29,9 +30,22 @@ const SignInFailedModal = ({ }) } onLoginPress={async () => { - await signOut(); - await signIn(); + const isIdentityEnabled = + remoteConfigService.getBoolean('identity_enabled'); + if (isIdentityEnabled) { + navigation.navigate(RouteNames.Settings, { + screen: RouteNames.SignIn, + }); + } else { + await signOut(); + await signIn(); + } }} + onFaqPress={() => + navigation.navigate(RouteNames.Settings, { + screen: RouteNames.FAQ, + }) + } close={() => navigation.navigate(RouteNames.Issue)} /> diff --git a/projects/Mallard/src/components/Modals/SignInModal.tsx b/projects/Mallard/src/components/Modals/SignInModal.tsx index 7210ab4e3..1e095562b 100644 --- a/projects/Mallard/src/components/Modals/SignInModal.tsx +++ b/projects/Mallard/src/components/Modals/SignInModal.tsx @@ -1,9 +1,10 @@ import { useNavigation } from '@react-navigation/native'; import React from 'react'; +import { useOkta } from 'src/hooks/use-okta-sign-in'; import { RouteNames } from 'src/navigation/NavigationModels'; +import { remoteConfigService } from 'src/services/remote-config'; import { CenterWrapper } from '../CenterWrapper/CenterWrapper'; import { SignInModalCard } from '../sign-in-modal-card'; -import { useOkta } from 'src/hooks/use-okta-sign-in'; const SignInModal = () => { const { navigate } = useNavigation(); @@ -12,8 +13,22 @@ const SignInModal = () => { navigate(RouteNames.Issue)} - onLoginPress={signIn} - close={() => navigate(RouteNames.Article)} + onLoginPress={() => { + const isIdentityEnabled = + remoteConfigService.getBoolean('identity_enabled'); + isIdentityEnabled + ? navigate(RouteNames.Settings, { + screen: RouteNames.SignIn, + }) + : signIn(); + }} + close={() => { + const isIdentityEnabled = + remoteConfigService.getBoolean('identity_enabled'); + isIdentityEnabled + ? navigate(RouteNames.Issue) + : navigate(RouteNames.Article); + }} /> ); diff --git a/projects/Mallard/src/components/Modals/SubFoundModal.tsx b/projects/Mallard/src/components/Modals/SubFoundModal.tsx index c422c17d6..329de2050 100644 --- a/projects/Mallard/src/components/Modals/SubFoundModal.tsx +++ b/projects/Mallard/src/components/Modals/SubFoundModal.tsx @@ -2,16 +2,21 @@ import { useNavigation } from '@react-navigation/native'; import React from 'react'; import { Copy } from 'src/helpers/words'; import { RouteNames } from 'src/navigation/NavigationModels'; +import { remoteConfigService } from 'src/services/remote-config'; import { CenterWrapper } from '../CenterWrapper/CenterWrapper'; import { CardAppearance, OnboardingCard } from '../onboarding/onboarding-card'; const SubFoundModalCard = () => { - const { goBack } = useNavigation(); + const { goBack, navigate } = useNavigation(); return ( goBack()} + onDismissThisCard={() => { + const isIdentityEnabled = + remoteConfigService.getBoolean('identity_enabled'); + isIdentityEnabled ? navigate(RouteNames.Issue) : goBack(); + }} subtitle={Copy.subFound.subtitle} appearance={CardAppearance.Blue} size="small" diff --git a/projects/Mallard/src/components/Modals/SubNotFoundModal.tsx b/projects/Mallard/src/components/Modals/SubNotFoundModal.tsx index b6cc514f4..8aedbf82e 100644 --- a/projects/Mallard/src/components/Modals/SubNotFoundModal.tsx +++ b/projects/Mallard/src/components/Modals/SubNotFoundModal.tsx @@ -2,6 +2,7 @@ import { useNavigation } from '@react-navigation/native'; import React from 'react'; import { useOkta } from 'src/hooks/use-okta-sign-in'; import { RouteNames } from 'src/navigation/NavigationModels'; +import { remoteConfigService } from 'src/services/remote-config'; import { CenterWrapper } from '../CenterWrapper/CenterWrapper'; import { SubNotFoundModalCard } from '../sub-not-found-modal-card'; @@ -12,7 +13,15 @@ const SubNotFoundModal = () => { navigate(RouteNames.Issue)} - onLoginPress={signIn} + onLoginPress={() => { + const isIdentityEnabled = + remoteConfigService.getBoolean('identity_enabled'); + isIdentityEnabled + ? navigate(RouteNames.Settings, { + screen: RouteNames.SignIn, + }) + : signIn(); + }} onOpenCASLogin={() => navigate(RouteNames.Settings, { screen: RouteNames.CasSignIn, diff --git a/projects/Mallard/src/components/SignInFailedModalCard.tsx b/projects/Mallard/src/components/SignInFailedModalCard.tsx index 68381a95e..36f9a101d 100644 --- a/projects/Mallard/src/components/SignInFailedModalCard.tsx +++ b/projects/Mallard/src/components/SignInFailedModalCard.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { StyleSheet, View } from 'react-native'; import { Copy } from 'src/helpers/words'; +import { remoteConfigService } from 'src/services/remote-config'; import { metrics } from 'src/theme/spacing'; import { ModalButton } from './Button/ModalButton'; import { CardAppearance, OnboardingCard } from './onboarding/onboarding-card'; @@ -16,22 +17,53 @@ const styles = StyleSheet.create({ }, }); +interface FailureModalText { + title: string; + bodyCopy: string; + tryAgainText: string; +} + +const failureModalText = ( + isAppleRelayEmail: boolean, + email: string, +): FailureModalText => { + return isAppleRelayEmail + ? { + title: Copy.failedSignIn.appleRelayTitle, + bodyCopy: Copy.failedSignIn.appleRelayBody, + tryAgainText: Copy.failedSignIn.appleRelayRetry, + } + : { + title: Copy.failedSignIn.title, + bodyCopy: Copy.failedSignIn.body.replace('%email%', email), + tryAgainText: Copy.failedSignIn.retryButtonTitle, + }; +}; + const SignInFailedModalCard = ({ close, onLoginPress, onOpenCASLogin, onDismiss, + onFaqPress, email, }: { close: () => void; onLoginPress: () => void; onOpenCASLogin: () => void; onDismiss: () => void; + onFaqPress?: () => void; email: string; }) => { + const isIdentityEnabled = + remoteConfigService.getBoolean('identity_enabled'); + const isAppleRelayEmail = email.includes('privaterelay.appleid.com'); + const modalText = failureModalText(isAppleRelayEmail, email); return ( { close(); @@ -41,7 +73,9 @@ const SignInFailedModalCard = ({ bottomContent={ <> - {Copy.failedSignIn.body.replace('%email%', email)} + {isIdentityEnabled + ? modalText.bodyCopy + : Copy.failedSignIn.body.replace('%email%', email)} @@ -51,7 +85,9 @@ const SignInFailedModalCard = ({ onLoginPress(); }} > - {Copy.failedSignIn.retryButtonTitle} + {isIdentityEnabled + ? modalText.tryAgainText + : Copy.failedSignIn.retryButtonTitle} { @@ -61,6 +97,16 @@ const SignInFailedModalCard = ({ > Activate with subscriber ID + {isAppleRelayEmail && ( + { + close(); + onFaqPress?.(); + }} + > + How can I sign in with Apple? + + )} diff --git a/projects/Mallard/src/components/sub-not-found-modal-card.tsx b/projects/Mallard/src/components/sub-not-found-modal-card.tsx index f026eeb24..da386229f 100644 --- a/projects/Mallard/src/components/sub-not-found-modal-card.tsx +++ b/projects/Mallard/src/components/sub-not-found-modal-card.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { Copy } from 'src/helpers/words'; +import { remoteConfigService } from 'src/services/remote-config'; import { ModalButton } from './Button/ModalButton'; import { CardAppearance, OnboardingCard } from './onboarding/onboarding-card'; @@ -28,6 +29,9 @@ const SubNotFoundModalCard = ({ <> { + const isIdentityEnabled = + remoteConfigService.getBoolean('identity_enabled'); + isIdentityEnabled && close(); onLoginPress(); }} > diff --git a/projects/Mallard/src/hooks/use-login-overlay.tsx b/projects/Mallard/src/hooks/use-login-overlay.tsx index 2cb19ddbd..d37ac596b 100644 --- a/projects/Mallard/src/hooks/use-login-overlay.tsx +++ b/projects/Mallard/src/hooks/use-login-overlay.tsx @@ -1,17 +1,24 @@ import { useNavigation } from '@react-navigation/native'; import { useEffect } from 'react'; -import { useAccess, useOktaData } from 'src/authentication/AccessContext'; +import { + useAccess, + useIdentity, + useOktaData, +} from 'src/authentication/AccessContext'; import { RouteNames } from 'src/navigation/NavigationModels'; const useLoginOverlay = () => { const { navigate } = useNavigation(); const canAccess = useAccess(); const oktaData = useOktaData(); + const idData = useIdentity(); useEffect(() => { if (!canAccess) { const id = setTimeout(() => { - if (oktaData) { + if (idData) { + navigate(RouteNames.SubNotFoundModal); + } else if (oktaData) { navigate(RouteNames.SignInFailedModal, { emailAddress: oktaData.userDetails.preferred_username, }); diff --git a/projects/Mallard/src/hooks/use-okta-sign-in.tsx b/projects/Mallard/src/hooks/use-okta-sign-in.tsx index e8f17d4b2..fefc71651 100644 --- a/projects/Mallard/src/hooks/use-okta-sign-in.tsx +++ b/projects/Mallard/src/hooks/use-okta-sign-in.tsx @@ -34,7 +34,6 @@ const useOkta = () => { } setIsLoading(false); } catch (e) { - console.log(e); const appleErrorString = getErrorString(e); appleErrorString && setError(appleErrorString); setIsLoading(false); diff --git a/projects/Mallard/src/screens/settings-screen.tsx b/projects/Mallard/src/screens/settings-screen.tsx index e9b8bc38e..a309656b5 100644 --- a/projects/Mallard/src/screens/settings-screen.tsx +++ b/projects/Mallard/src/screens/settings-screen.tsx @@ -28,6 +28,7 @@ import { useIsWeatherShown } from 'src/hooks/use-weather-provider'; import type { SettingsStackParamList } from 'src/navigation/NavigationModels'; import { RouteNames } from 'src/navigation/NavigationModels'; import { BetaButtonOption } from 'src/screens/settings/join-beta-button'; +import { remoteConfigService } from 'src/services/remote-config'; import { WithAppAppearance } from 'src/theme/appearance'; const MiscSettingsList = () => { @@ -162,8 +163,12 @@ const SettingsScreen = () => { const isLoggedInWithOkta = oktaData ? oktaData.userDetails.preferred_username : false; + const isLoggedInWithIdentity = identityData + ? identityData.userDetails.primaryEmailAddress + : false; - const canDisplayBetaButton = !iapData && isLoggedInWithOkta; + const canDisplayBetaButton = + !iapData && (isLoggedInWithOkta || isLoggedInWithIdentity); const buildNumber = DeviceInfo.getBuildNumber(); const { isUsingProdDevtools, setIsUsingProdDevTools } = useIsUsingProdDevtools(); @@ -220,7 +225,15 @@ const SettingsScreen = () => { accessible={true} accessibilityRole="button" username={username()} - signIn={signIn} + signIn={() => { + const isIdentityEnabled = + remoteConfigService.getBoolean( + 'identity_enabled', + ); + isIdentityEnabled + ? navigation.navigate(RouteNames.SignIn) + : signIn(); + }} signOut={() => { signOut(); signOutIdentity(); diff --git a/projects/Mallard/src/screens/settings/already-subscribed-screen.tsx b/projects/Mallard/src/screens/settings/already-subscribed-screen.tsx index 92e3de989..eac2d6d85 100644 --- a/projects/Mallard/src/screens/settings/already-subscribed-screen.tsx +++ b/projects/Mallard/src/screens/settings/already-subscribed-screen.tsx @@ -11,6 +11,7 @@ import { List } from 'src/components/lists/list'; import { Copy } from 'src/helpers/words'; import { useOkta } from 'src/hooks/use-okta-sign-in'; import { RouteNames } from 'src/navigation/NavigationModels'; +import { remoteConfigService } from 'src/services/remote-config'; import { WithAppAppearance } from 'src/theme/appearance'; const AlreadySubscribedScreen = () => { @@ -38,7 +39,17 @@ const AlreadySubscribedScreen = () => { key: 'Sign in to activate', title: Copy.alreadySubscribed .signInTitle, - onPress: signIn, + onPress: () => { + const isIdentityEnabled = + remoteConfigService.getBoolean( + 'identity_enabled', + ); + isIdentityEnabled + ? navigation.navigate( + RouteNames.SignIn, + ) + : signIn(); + }, proxy: rightChevronIcon, linkWeight: 'regular', }, diff --git a/projects/Mallard/src/services/remote-config.ts b/projects/Mallard/src/services/remote-config.ts index f3c3beae0..8607a3238 100644 --- a/projects/Mallard/src/services/remote-config.ts +++ b/projects/Mallard/src/services/remote-config.ts @@ -17,6 +17,7 @@ const remoteConfigDefaults = { generate_share_url: true, download_parallel_ssr_bundle: false, rating: false, + identity_enabled: true, }; const RemoteConfigProperties = [ @@ -26,6 +27,7 @@ const RemoteConfigProperties = [ 'generate_share_url', 'download_parallel_ssr_bundle', 'rating', + 'identity_enabled', ] as const; type RemoteConfigProperty = typeof RemoteConfigProperties[number];