Skip to content

Commit

Permalink
feat: Identity enabled remote config
Browse files Browse the repository at this point in the history
  • Loading branch information
jimhunty committed Jun 28, 2023
1 parent 8ea5139 commit f5660ba
Show file tree
Hide file tree
Showing 14 changed files with 155 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<AuthResult<any>> => {
try {
Expand All @@ -24,7 +24,7 @@ const authWithTokens = async (mtoken: string): Promise<AuthResult<any>> => {
}),
);
} catch (e) {
console.log('AUTH WITH TOKENS ERROR: ', e);
return InvalidResult();
}
};

Expand All @@ -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 () => {
Expand Down
4 changes: 2 additions & 2 deletions projects/Mallard/src/authentication/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
39 changes: 4 additions & 35 deletions projects/Mallard/src/authentication/services/okta.ts
Original file line number Diff line number Diff line change
@@ -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({
Expand All @@ -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);
}
};

Expand All @@ -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 };
18 changes: 16 additions & 2 deletions projects/Mallard/src/components/Modals/SignInFailedModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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)}
/>
</CenterWrapper>
Expand Down
21 changes: 18 additions & 3 deletions projects/Mallard/src/components/Modals/SignInModal.tsx
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -12,8 +13,22 @@ const SignInModal = () => {
<CenterWrapper>
<SignInModalCard
onDismiss={() => 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);
}}
/>
</CenterWrapper>
);
Expand Down
9 changes: 7 additions & 2 deletions projects/Mallard/src/components/Modals/SubFoundModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<CenterWrapper>
<OnboardingCard
title={Copy.subFound.title}
onDismissThisCard={() => goBack()}
onDismissThisCard={() => {
const isIdentityEnabled =
remoteConfigService.getBoolean('identity_enabled');
isIdentityEnabled ? navigate(RouteNames.Issue) : goBack();
}}
subtitle={Copy.subFound.subtitle}
appearance={CardAppearance.Blue}
size="small"
Expand Down
11 changes: 10 additions & 1 deletion projects/Mallard/src/components/Modals/SubNotFoundModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -12,7 +13,15 @@ const SubNotFoundModal = () => {
<CenterWrapper>
<SubNotFoundModalCard
close={() => 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,
Expand Down
52 changes: 49 additions & 3 deletions projects/Mallard/src/components/SignInFailedModalCard.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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 (
<OnboardingCard
title={Copy.failedSignIn.title}
title={
isIdentityEnabled ? modalText.title : Copy.failedSignIn.title
}
appearance={CardAppearance.Blue}
onDismissThisCard={() => {
close();
Expand All @@ -41,7 +73,9 @@ const SignInFailedModalCard = ({
bottomContent={
<>
<UiBodyCopy weight="bold">
{Copy.failedSignIn.body.replace('%email%', email)}
{isIdentityEnabled
? modalText.bodyCopy
: Copy.failedSignIn.body.replace('%email%', email)}
</UiBodyCopy>
<View style={styles.bottomContentContainer}>
<View>
Expand All @@ -51,7 +85,9 @@ const SignInFailedModalCard = ({
onLoginPress();
}}
>
{Copy.failedSignIn.retryButtonTitle}
{isIdentityEnabled
? modalText.tryAgainText
: Copy.failedSignIn.retryButtonTitle}
</ModalButton>
<ModalButton
onPress={() => {
Expand All @@ -61,6 +97,16 @@ const SignInFailedModalCard = ({
>
Activate with subscriber ID
</ModalButton>
{isAppleRelayEmail && (
<ModalButton
onPress={() => {
close();
onFaqPress?.();
}}
>
How can I sign in with Apple?
</ModalButton>
)}
</View>
</View>
</>
Expand Down
4 changes: 4 additions & 0 deletions projects/Mallard/src/components/sub-not-found-modal-card.tsx
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -28,6 +29,9 @@ const SubNotFoundModalCard = ({
<>
<ModalButton
onPress={() => {
const isIdentityEnabled =
remoteConfigService.getBoolean('identity_enabled');
isIdentityEnabled && close();
onLoginPress();
}}
>
Expand Down
11 changes: 9 additions & 2 deletions projects/Mallard/src/hooks/use-login-overlay.tsx
Original file line number Diff line number Diff line change
@@ -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,
});
Expand Down
1 change: 0 additions & 1 deletion projects/Mallard/src/hooks/use-okta-sign-in.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ const useOkta = () => {
}
setIsLoading(false);
} catch (e) {
console.log(e);
const appleErrorString = getErrorString(e);
appleErrorString && setError(appleErrorString);
setIsLoading(false);
Expand Down
Loading

0 comments on commit f5660ba

Please sign in to comment.