Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SIW-1455] Reissuing expired or expiring eID #6580

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
aa53709
feat: reissuing eid
RiccardoMolinari95 Dec 23, 2024
a0f6f55
refactor: prettify
RiccardoMolinari95 Dec 23, 2024
38afaa3
refactor: reintroduced store of real eid and credential and not mocked
RiccardoMolinari95 Dec 23, 2024
503d15d
test: add test eid reissuing
RiccardoMolinari95 Dec 23, 2024
4daa05b
test: add tests for back button in UserIdentification: ModeSelection
RiccardoMolinari95 Dec 23, 2024
903cb6c
refactor: prettify
RiccardoMolinari95 Dec 23, 2024
d9f0a5c
Merge commit '80d7bd563c8810002384c28a4677de923403bbd2' into SIW-1455…
RiccardoMolinari95 Dec 24, 2024
50f94fa
fix lint
RiccardoMolinari95 Dec 24, 2024
1ba545e
chore: check wia before start eid reisuuing flow
RiccardoMolinari95 Jan 3, 2025
c517752
refactor: deleted assignWalletInstanceAttestationToContext action
RiccardoMolinari95 Jan 3, 2025
d12f249
fix: lint
RiccardoMolinari95 Jan 3, 2025
e8d82be
chore:prettify
RiccardoMolinari95 Jan 3, 2025
7f44f95
test: fix
RiccardoMolinari95 Jan 3, 2025
1159e10
Merge branch 'master' into SIW-1455-credential-expiry-and-reissuing
RiccardoMolinari95 Jan 7, 2025
ef32d1f
Merge branch 'master' into SIW-1455-credential-expiry-and-reissuing
RiccardoMolinari95 Jan 8, 2025
2ca518f
refactor: screen props in ItwIdentificationModeSelectionScreen
RiccardoMolinari95 Jan 8, 2025
decb8ab
chore: prettify
RiccardoMolinari95 Jan 8, 2025
b72bffd
chore: deleted console log
RiccardoMolinari95 Jan 8, 2025
95f5372
test: fix ItwIdentificationModeSelectionScreen test
RiccardoMolinari95 Jan 8, 2025
d19bcf8
test: removed unused prop
RiccardoMolinari95 Jan 8, 2025
9cba6a7
Merge branch 'master' into SIW-1455-credential-expiry-and-reissuing
RiccardoMolinari95 Jan 8, 2025
c2d1773
Merge branch 'master' into SIW-1455-credential-expiry-and-reissuing
RiccardoMolinari95 Jan 8, 2025
151e558
Merge commit '7e5ca4a918ec36b82e77837987da34db647bffdf' into SIW-1455…
RiccardoMolinari95 Jan 13, 2025
f856774
Merge branch 'master' into SIW-1455-credential-expiry-and-reissuing
RiccardoMolinari95 Jan 13, 2025
0fb11ee
refactor: delete unnecessary state in eid machine
RiccardoMolinari95 Jan 13, 2025
cefa620
Merge branch 'master' into SIW-1455-credential-expiry-and-reissuing
RiccardoMolinari95 Jan 13, 2025
29346af
Merge branch 'master' into SIW-1455-credential-expiry-and-reissuing
RiccardoMolinari95 Jan 14, 2025
a7793e6
Merge branch 'master' into SIW-1455-credential-expiry-and-reissuing
RiccardoMolinari95 Jan 14, 2025
5ebd8ae
chore: restored state DisplayingPreview as before
RiccardoMolinari95 Jan 14, 2025
16eb065
Merge branch 'master' into SIW-1455-credential-expiry-and-reissuing
RiccardoMolinari95 Jan 14, 2025
46e0c48
Merge branch 'master' into SIW-1455-credential-expiry-and-reissuing
mastro993 Jan 14, 2025
1df9414
Merge branch 'master' into SIW-1455-credential-expiry-and-reissuing
RiccardoMolinari95 Jan 15, 2025
00f772a
chore: guard with and in eid machine WalletInstanceAttestationObtainm…
RiccardoMolinari95 Jan 15, 2025
49df2b3
test: fix
RiccardoMolinari95 Jan 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions locales/en/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3482,6 +3482,7 @@ features:
valid: L'ultima verifica è del {{date}}.
expiring: Verifica la tua identità entro il {{date}}.
expired: È necessario un rapido passaggio di verifica per continuare a usare Documenti su IO.
action: Inizia
MDL:
expiring:
title: "Patente su IO: documento in scadenza"
Expand Down Expand Up @@ -3535,6 +3536,13 @@ features:
openPdf: "Show document"
shareButton: Save or share
fiscalCode: Your Fiscal Code
eid:
verificationExpired:
title: Verifica la tua identità
contentStart: "È un passaggio di sicurezza necessario per continuare ad usare "
contentBold: Documenti su IO
contentEnd: "."
primaryAction: Inizia
trustmark:
cta: Mostra certificato di autenticità
description: Mostra il QR Code per attestare l’autenticità del documento quando ti viene richiesto.
Expand Down
8 changes: 8 additions & 0 deletions locales/it/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3482,6 +3482,7 @@ features:
valid: L'ultima verifica è del {{date}}.
expiring: Verifica la tua identità entro il {{date}}.
expired: È necessario un rapido passaggio di verifica per continuare a usare Documenti su IO.
action: Inizia
MDL:
expiring:
title: "Patente su IO: documento in scadenza"
Expand Down Expand Up @@ -3535,6 +3536,13 @@ features:
openPdf: "Mostra documento"
shareButton: Salva o condividi
fiscalCode: Il tuo Codice Fiscale
eid:
verificationExpired:
title: Verifica la tua identità
contentStart: "È un passaggio di sicurezza necessario per continuare ad usare "
contentBold: Documenti su IO
contentEnd: "."
primaryAction: Inizia
trustmark:
cta: Mostra certificato di autenticità
description: Mostra il QR Code per attestare l’autenticità del documento quando ti viene richiesto.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const ItwEidInfoBottomSheetContent = ({
</React.Fragment>
))}
</View>
<ItwEidLifecycleAlert />
<ItwEidLifecycleAlert navigation={navigation} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use the useIONavigation hook inside the component instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had some problems using useIONavigation inside the ItwEidLifecycleAlert component, so I preferred to pass it from the parent component to ItwEidLifecycleAlert as a prop since it was already available.
Let me know if this is okay for you, or if you would like me to try integrating it directly into ItwEidLifecycleAlert

<IOMarkdown
content={I18n.t(
"features.itWallet.presentation.bottomSheets.eidInfo.contentBottom"
Expand Down
27 changes: 24 additions & 3 deletions ts/features/itwallet/common/components/ItwEidLifecycleAlert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
ItwJwtCredentialStatus,
StoredCredential
} from "../utils/itwTypesUtils";
import { useIONavigation } from "../../../../navigation/params/AppParamsList";
import { ITW_ROUTES } from "../../navigation/routes";

const defaultLifecycleStatus: Array<ItwJwtCredentialStatus> = [
"valid",
Expand All @@ -27,17 +29,28 @@ type Props = {
* The eID statuses that will render the alert.
*/
lifecycleStatus?: Array<ItwJwtCredentialStatus>;
navigation: ReturnType<typeof useIONavigation>;
};

/**
* This component renders an alert that displays information on the eID status.
*/
export const ItwEidLifecycleAlert = ({
lifecycleStatus = defaultLifecycleStatus
lifecycleStatus = defaultLifecycleStatus,
navigation
}: Props) => {
const eidOption = useIOSelector(itwCredentialsEidSelector);
const maybeEidStatus = useIOSelector(itwCredentialsEidStatusSelector);

const startEidReissuing = () => {
navigation.navigate(ITW_ROUTES.MAIN, {
screen: ITW_ROUTES.IDENTIFICATION.MODE_SELECTION,
params: {
eidReissuing: true
}
});
};

const Content = ({
eid,
eidStatus
Expand Down Expand Up @@ -73,14 +86,22 @@ export const ItwEidLifecycleAlert = ({
{
date: format(eid.jwt.expiration, "DD-MM-YYYY")
}
)
),
action: I18n.t(
"features.itWallet.presentation.bottomSheets.eidInfo.alert.action"
),
onPress: startEidReissuing
},
jwtExpired: {
testID: "itwEidLifecycleAlertTestID_jwtExpired",
variant: "error",
content: I18n.t(
"features.itWallet.presentation.bottomSheets.eidInfo.alert.expired"
)
),
action: I18n.t(
"features.itWallet.presentation.bottomSheets.eidInfo.alert.action"
),
onPress: startEidReissuing
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
VStack
} from "@pagopa/io-app-design-system";
import React, { useCallback, useMemo } from "react";
import { useFocusEffect } from "@react-navigation/native";
import { Route, useFocusEffect, useRoute } from "@react-navigation/native";
import I18n from "../../../../i18n";
import { useIOSelector } from "../../../../store/hooks";
import { cieFlowForDevServerEnabled } from "../../../cieLogin/utils";
Expand All @@ -17,6 +17,13 @@ import {
import { itwDisabledIdentificationMethodsSelector } from "../../../../store/reducers/backendStatus/remoteConfig";
import { IOScrollViewWithLargeHeader } from "../../../../components/ui/IOScrollViewWithLargeHeader";
import { isCIEAuthenticationSupportedSelector } from "../../machine/eid/selectors";
import { ITW_ROUTES } from "../../navigation/routes";

export type ItwIdentificationModeSelectionScreenNavigationParams =
| {
eidReissuing?: boolean;
}
| undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export type ItwIdentificationModeSelectionScreenNavigationParams =
| {
eidReissuing?: boolean;
}
| undefined;
export type ItwIdentificationModeSelectionScreenNavigationParams = {
eidReissuing?: boolean;
};
export type ScreenProps = IOStackNavigationRouteProps<
ItwParamsList,
"ITW_IDENTIFICATION_MODE_SELECTION"
>

I believe this approach is more appropriate because:

  • the optional eidReissuing parameter is not hidden. This makes it clear to anyone using this screen that the parameter exists and is optional
  • by defining a dedicated type for the screen props we are improving readability and maintainability

What do you think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you're right! Please check here 2ca518f ( console.log deleted here b72bffd)


export const ItwIdentificationModeSelectionScreen = () => {
const machineRef = ItwEidIssuanceMachineContext.useActorRef();
Expand Down Expand Up @@ -45,6 +52,24 @@ export const ItwIdentificationModeSelectionScreen = () => {
[isCieAuthenticationSupported]
);

const route =
useRoute<
Route<
typeof ITW_ROUTES.IDENTIFICATION.MODE_SELECTION,
ItwIdentificationModeSelectionScreenNavigationParams
>
>();

const { eidReissuing } = route.params || {};

useFocusEffect(
useCallback(() => {
if (eidReissuing) {
machineRef.send({ type: "start-reissuing" });
}
}, [eidReissuing, machineRef])
);

useFocusEffect(trackItWalletIDMethod);

const handleSpidPress = useCallback(() => {
Expand Down
188 changes: 186 additions & 2 deletions ts/features/itwallet/machine/eid/__tests__/machine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ describe("itwEidIssuanceMachine", () => {
const trackWalletInstanceCreation = jest.fn();
const trackWalletInstanceRevocation = jest.fn();
const revokeWalletInstance = jest.fn();
const isReissuingAndSessionExpired = jest.fn();

const mockedMachine = itwEidIssuanceMachine.provide({
actions: {
Expand Down Expand Up @@ -94,7 +95,10 @@ describe("itwEidIssuanceMachine", () => {
resetWalletInstance,
trackWalletInstanceCreation,
trackWalletInstanceRevocation,
onInit: assign(onInit)
onInit: assign(onInit),
setIsReissuing: assign({
isReissuing: true
})
},
actors: {
createWalletInstance: fromPromise<string>(createWalletInstance),
Expand All @@ -116,7 +120,8 @@ describe("itwEidIssuanceMachine", () => {
issuedEidMatchesAuthenticatedUser,
isSessionExpired,
isOperationAborted,
hasValidWalletInstanceAttestation
hasValidWalletInstanceAttestation,
isReissuingAndSessionExpired
}
});

Expand Down Expand Up @@ -985,6 +990,185 @@ describe("itwEidIssuanceMachine", () => {
expect(actor.getSnapshot().value).toStrictEqual("Idle");
});

it("Should obtain an eID (SPID), reissuing mode", async () => {
// The wallet instance and attestation already exist
const initialContext = {
...InitialContext,
integrityKeyTag: T_INTEGRITY_KEY,
walletInstanceAttestation: T_WIA
};

const actor = createActor(mockedMachine);
actor.start();

// eslint-disable-next-line functional/immutable-data
actor.getSnapshot().context = initialContext;

hasValidWalletInstanceAttestation.mockImplementation(() => true);

await waitFor(() => expect(onInit).toHaveBeenCalledTimes(1));

expect(actor.getSnapshot().value).toStrictEqual("Idle");
expect(actor.getSnapshot().tags).toStrictEqual(new Set());

actor.send({ type: "start-reissuing" });

expect(actor.getSnapshot().value).toStrictEqual({
UserIdentification: "ModeSelection"
});

expect(actor.getSnapshot().context).toStrictEqual<Context>({
...initialContext,
isReissuing: true
});

/**
* Choose SPID as identification mode
*/

actor.send({ type: "select-identification-mode", mode: "spid" });

expect(actor.getSnapshot().value).toStrictEqual({
UserIdentification: {
Spid: "IdpSelection"
}
});
expect(actor.getSnapshot().tags).toStrictEqual(new Set());
expect(navigateToIdpSelectionScreen).toHaveBeenCalledTimes(1);

/**
* Choose first IDP in list for SPID identification
*/

startAuthFlow.mockImplementation(() => Promise.resolve({}));

requestEid.mockImplementation(() =>
Promise.resolve(ItwStoredCredentialsMocks.eid)
);

issuedEidMatchesAuthenticatedUser.mockImplementation(() => true);

actor.send({ type: "select-spid-idp", idp: idps[0] });

expect(actor.getSnapshot().value).toStrictEqual({
UserIdentification: {
Spid: "StartingSpidAuthFlow"
}
});

expect(actor.getSnapshot().context).toStrictEqual<Context>({
...initialContext,
integrityKeyTag: T_INTEGRITY_KEY,
walletInstanceAttestation: T_WIA,
isReissuing: true,
identification: {
mode: "spid",
idpId: idps[0].id
}
});

expect(actor.getSnapshot().tags).toStrictEqual(new Set([ItwTags.Loading]));

await waitFor(() => expect(startAuthFlow).toHaveBeenCalledTimes(1));

expect(actor.getSnapshot().value).toStrictEqual({
UserIdentification: {
Spid: "CompletingSpidAuthFlow"
}
});

actor.send({
type: "user-identification-completed",
authRedirectUrl: "http://test.it"
});

expect(actor.getSnapshot().value).toStrictEqual({
Issuance: "RequestingEid"
});

expect(actor.getSnapshot().tags).toStrictEqual(new Set([ItwTags.Loading]));
expect(actor.getSnapshot().context).toMatchObject({
authenticationContext: {
callbackUrl: "http://test.it"
}
});
expect(navigateToEidPreviewScreen).toHaveBeenCalledTimes(1);

// EID obtained

// eslint-disable-next-line sonarjs/no-identical-functions
await waitFor(() =>
expect(actor.getSnapshot().value).toStrictEqual({
Issuance: "DisplayingPreview"
})
);

actor.send({ type: "add-to-wallet" });

expect(storeEidCredential).toHaveBeenCalledTimes(1);
expect(setWalletInstanceToValid).toHaveBeenCalledTimes(1);
expect(navigateToWallet).toHaveBeenCalledTimes(1);

expect(actor.getSnapshot().context).toStrictEqual<Context>({
...initialContext,
integrityKeyTag: T_INTEGRITY_KEY,
walletInstanceAttestation: T_WIA,
isReissuing: true,
identification: {
mode: "spid",
idpId: idps[0].id
},
authenticationContext: expect.objectContaining({
callbackUrl: "http://test.it"
}),
eid: ItwStoredCredentialsMocks.eid
});
});

it("Should go back to Idle state if isReissuing is true", async () => {
const initialSnapshot: MachineSnapshot = createActor(
itwEidIssuanceMachine
).getSnapshot();

const snapshot: MachineSnapshot = _.merge(undefined, initialSnapshot, {
value: { UserIdentification: "ModeSelection" },
context: {
isReissuing: true
}
} as MachineSnapshot);

const actor = createActor(mockedMachine, {
snapshot
});
actor.start();

actor.send({ type: "back" });

expect(actor.getSnapshot().value).toStrictEqual("Idle");
});

it("Should go back to IpzsPrivacyAcceptance state if isReissuing is false", async () => {
const initialSnapshot: MachineSnapshot = createActor(
itwEidIssuanceMachine
).getSnapshot();

const snapshot: MachineSnapshot = _.merge(undefined, initialSnapshot, {
value: { UserIdentification: "ModeSelection" },
context: {
isReissuing: false
}
} as MachineSnapshot);

const actor = createActor(mockedMachine, {
snapshot
});
actor.start();

actor.send({ type: "back" });

expect(actor.getSnapshot().value).toStrictEqual("IpzsPrivacyAcceptance");
});

it("should cleanup integrity key tag and fail when obtaining Wallet Instance Attestation fails", async () => {
const actor = createActor(mockedMachine);
actor.start();
Expand Down
4 changes: 3 additions & 1 deletion ts/features/itwallet/machine/eid/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export type Context = {
authenticationContext: AuthenticationContext | undefined;
eid: StoredCredential | undefined;
failure: IssuanceFailure | undefined;
isReissuing: boolean;
};

export const InitialContext: Context = {
Expand All @@ -50,5 +51,6 @@ export const InitialContext: Context = {
identification: undefined,
authenticationContext: undefined,
eid: undefined,
failure: undefined
failure: undefined,
isReissuing: false
};
Loading
Loading