diff --git a/ts/features/cieLogin/analytics/index.ts b/ts/features/cieLogin/analytics/index.ts index 44ded75e7ec..b7d49d3eae4 100644 --- a/ts/features/cieLogin/analytics/index.ts +++ b/ts/features/cieLogin/analytics/index.ts @@ -7,3 +7,10 @@ export const trackCieIdNoWhitelistUrl = (url: string) => { buildEventProperties("KO", undefined, { url }) ); }; + +export const trackCieIdSecurityLevelMismatch = () => { + void mixpanelTrack( + "SECURITY_LEVEL_MISMATCH", + buildEventProperties("TECH", undefined) + ); +}; diff --git a/ts/features/cieLogin/sagas/trackLevelSecuritySaga.ts b/ts/features/cieLogin/sagas/trackLevelSecuritySaga.ts new file mode 100644 index 00000000000..70e21e47a6d --- /dev/null +++ b/ts/features/cieLogin/sagas/trackLevelSecuritySaga.ts @@ -0,0 +1,32 @@ +import { select } from "typed-redux-saga/macro"; +import * as O from "fp-ts/Option"; +import { pipe } from "fp-ts/lib/function"; +import { sequenceT } from "fp-ts/lib/Apply"; +import { cieIDSelectedSecurityLevelSelector } from "../store/selectors"; +import { idpSelector } from "../../../store/reducers/authentication"; +import { IdpCIE_ID } from "../../../hooks/useNavigateToLoginMethod"; +import { trackCieIdSecurityLevelMismatch } from "../analytics"; +import { PublicSession } from "../../../../definitions/session_manager/PublicSession"; + +export function* shouldTrackLevelSecurityMismatchSaga( + maybeSessionInformation: O.Option +) { + const selectedSecurityLevel = yield* select( + cieIDSelectedSecurityLevelSelector + ); + const idpSelected = yield* select(idpSelector); + const selectedLevelMismatches = pipe( + sequenceT(O.Monad)(maybeSessionInformation, idpSelected), + O.chainNullableK( + ([session, idp]) => + selectedSecurityLevel && + idp.id === IdpCIE_ID.id && + !session.spidLevel?.includes(selectedSecurityLevel) + ), + O.getOrElse(() => false) + ); + + if (selectedLevelMismatches) { + trackCieIdSecurityLevelMismatch(); + } +} diff --git a/ts/features/cieLogin/store/actions/index.ts b/ts/features/cieLogin/store/actions/index.ts index 25465450ff9..23b8c799f52 100644 --- a/ts/features/cieLogin/store/actions/index.ts +++ b/ts/features/cieLogin/store/actions/index.ts @@ -1,4 +1,5 @@ import { ActionType, createStandardAction } from "typesafe-actions"; +import { SpidLevel } from "../../utils"; export const cieLoginEnableUat = createStandardAction("CIE_LOGIN_ENABLE_UAT")(); @@ -13,9 +14,13 @@ export const cieIDFeatureSetEnabled = createStandardAction( export const cieIDDisableTourGuide = createStandardAction( "CIE_ID_DISABLE_TOUR_GUIDE" )(); +export const cieIDSetSelectedSecurityLevel = createStandardAction( + "CIE_ID_SET_SELECTED_SECURITY_LEVEL" +)(); export type CieLoginConfigActions = | ActionType | ActionType | ActionType - | ActionType; + | ActionType + | ActionType; diff --git a/ts/features/cieLogin/store/reducers/index.ts b/ts/features/cieLogin/store/reducers/index.ts index 7b27a4dedde..6c69c544c16 100644 --- a/ts/features/cieLogin/store/reducers/index.ts +++ b/ts/features/cieLogin/store/reducers/index.ts @@ -11,16 +11,19 @@ import { merge } from "lodash"; import { cieIDDisableTourGuide, cieIDFeatureSetEnabled, + cieIDSetSelectedSecurityLevel, cieLoginDisableUat, cieLoginEnableUat } from "../actions"; import { Action } from "../../../../store/actions/types"; import { isDevEnv } from "../../../../utils/environment"; +import { SpidLevel } from "../../utils"; export type CieLoginState = { useUat: boolean; isCieIDFeatureEnabled: boolean; isCieIDTourGuideEnabled: boolean; + cieIDSelectedSecurityLevel?: SpidLevel; }; export const cieLoginInitialState = { @@ -54,6 +57,11 @@ const cieLoginReducer = ( ...state, isCieIDTourGuideEnabled: false }; + case getType(cieIDSetSelectedSecurityLevel): + return { + ...state, + cieIDSelectedSecurityLevel: action.payload + }; default: return state; } diff --git a/ts/features/cieLogin/store/selectors/index.ts b/ts/features/cieLogin/store/selectors/index.ts index e97ade93eab..3043a191240 100644 --- a/ts/features/cieLogin/store/selectors/index.ts +++ b/ts/features/cieLogin/store/selectors/index.ts @@ -29,3 +29,6 @@ export const isCieIDFFEnabledSelector = (state: GlobalState) => export const isCieIDTourGuideEnabledSelector = (state: GlobalState) => state.features.loginFeatures.cieLogin.isCieIDTourGuideEnabled && isCieIDFFEnabledSelector(state); + +export const cieIDSelectedSecurityLevelSelector = (state: GlobalState) => + state.features.loginFeatures.cieLogin.cieIDSelectedSecurityLevel; diff --git a/ts/hooks/useNavigateToLoginMethod.tsx b/ts/hooks/useNavigateToLoginMethod.tsx index 89f4b45b8a6..eefe96a5251 100644 --- a/ts/hooks/useNavigateToLoginMethod.tsx +++ b/ts/hooks/useNavigateToLoginMethod.tsx @@ -17,6 +17,7 @@ import { ChosenIdentifier, Identifier } from "../screens/authentication/OptInScreen"; +import { cieIDSetSelectedSecurityLevel } from "../features/cieLogin/store/actions"; export const IdpCIE: SpidIdp = { id: "cie", @@ -86,6 +87,7 @@ const useNavigateToLoginMethod = () => { const navigateToCieIdLoginScreen = useCallback( (spidLevel: SpidLevel = "SpidL2") => { dispatch(idpSelected(IdpCIE_ID)); + dispatch(cieIDSetSelectedSecurityLevel(spidLevel)); if (isCieIdAvailable(isCieUatEnabled) || cieFlowForDevServerEnabled) { const params = { diff --git a/ts/sagas/__tests__/initializeApplicationSaga.test.ts b/ts/sagas/__tests__/initializeApplicationSaga.test.ts index c775e4d947e..3e73b936d5e 100644 --- a/ts/sagas/__tests__/initializeApplicationSaga.test.ts +++ b/ts/sagas/__tests__/initializeApplicationSaga.test.ts @@ -49,6 +49,7 @@ import { trackKeychainFailures } from "../../utils/analytics"; import { checkSession } from "../startup/watchCheckSessionSaga"; import { formatRequestedTokenString } from "../../features/zendesk/utils"; import { checkPublicKeyAndBlockIfNeeded } from "../../features/lollipop/navigation"; +import { userFromSuccessLoginSelector } from "../../features/login/info/store/selectors"; const aSessionToken = "a_session_token" as SessionToken; const aSessionInfo = O.some({ @@ -293,6 +294,8 @@ describe("initializeApplicationSaga", () => { .next() .select(sessionInfoSelector) .next(aSessionInfo) + .select(userFromSuccessLoginSelector) + .next() .select(lollipopPublicKeySelector) .next(aPublicKey) .call( diff --git a/ts/sagas/startup.ts b/ts/sagas/startup.ts index 373a41e5c00..9eec137ee96 100644 --- a/ts/sagas/startup.ts +++ b/ts/sagas/startup.ts @@ -114,6 +114,8 @@ import { handleApplicationStartupTransientError } from "../features/startup/saga import { formatRequestedTokenString } from "../features/zendesk/utils"; import { isBlockingScreenSelector } from "../features/ingress/store/selectors"; import { watchLegacyTransactionSaga } from "../features/payments/transaction/store/saga"; +import { userFromSuccessLoginSelector } from "../features/login/info/store/selectors"; +import { shouldTrackLevelSecurityMismatchSaga } from "../features/cieLogin/sagas/trackLevelSecuritySaga"; import { startAndReturnIdentificationResult } from "./identification"; import { previousInstallationDataDeleteSaga } from "./installation"; import { @@ -377,6 +379,12 @@ export function* initializeApplicationSaga( } } + const userFromSuccessLogin = yield* select(userFromSuccessLoginSelector); + + if (userFromSuccessLogin) { + yield* call(shouldTrackLevelSecurityMismatchSaga, maybeSessionInformation); + } + const publicKey = yield* select(lollipopPublicKeySelector); // #LOLLIPOP_CHECK_BLOCK2_START