diff --git a/src/frontend/src/components/authenticateBox.ts b/src/frontend/src/components/authenticateBox.ts index a9328bb082..52c885218c 100644 --- a/src/frontend/src/components/authenticateBox.ts +++ b/src/frontend/src/components/authenticateBox.ts @@ -8,7 +8,11 @@ import { registerTentativeDevice } from "$src/flows/addDevice/welcomeView/regist import { idbRetrievePinIdentityMaterial } from "$src/flows/pin/idb"; import { usePin } from "$src/flows/pin/usePin"; import { useRecovery } from "$src/flows/recovery/useRecovery"; -import { getRegisterFlowOpts, registerFlow } from "$src/flows/register"; +import { + RegisterFlowOpts, + getRegisterFlowOpts, + registerFlow, +} from "$src/flows/register"; import { I18n } from "$src/i18n"; import { getAnchors, setAnchorUsed } from "$src/storage"; import { @@ -83,7 +87,7 @@ export const authenticateBox = async ({ authnMethod: "pin" | "passkey" | "recovery"; }> => { const promptAuth = () => - authenticateBoxFlow({ + authenticateBoxFlow({ i18n, templates, addDevice: (userNumber) => asNewDevice(connection, userNumber), @@ -159,7 +163,7 @@ const pinIdentityAuthenticatorValidity = async ({ /** Authentication box component which authenticates a user * to II or to another dapp */ -export const authenticateBoxFlow = async ({ +export const authenticateBoxFlow = async ({ i18n, templates, addDevice, @@ -179,7 +183,7 @@ export const authenticateBoxFlow = async ({ loginPasskey: ( userNumber: bigint ) => Promise< - LoginSuccess | AuthFail | WebAuthnFailed | UnknownUser | ApiError + LoginSuccess | AuthFail | WebAuthnFailed | UnknownUser | ApiError >; loginPinIdentityMaterial: ({ userNumber, @@ -189,8 +193,8 @@ export const authenticateBoxFlow = async ({ userNumber: bigint; pin: string; pinIdentityMaterial: I; - }) => Promise | BadPin>; - recover: () => Promise | { tag: "canceled" }>; + }) => Promise; + recover: () => Promise; retrievePinIdentityMaterial: ({ userNumber, }: { @@ -201,9 +205,9 @@ export const authenticateBoxFlow = async ({ userNumber: bigint; pinIdentityMaterial: I; }) => Promise<"valid" | "expired">; - registerFlowOpts: Parameters>[0]; + registerFlowOpts: RegisterFlowOpts; }): Promise< - | (LoginSuccess & { + | (LoginSuccess & { newAnchor: boolean; authnMethod: "pin" | "passkey" | "recovery"; }) @@ -215,7 +219,7 @@ export const authenticateBoxFlow = async ({ // The registration flow for a new identity const doRegister = async (): Promise< - | (LoginSuccess & { + | (LoginSuccess & { newAnchor: true; authnMethod: "pin" | "passkey" | "recovery"; }) @@ -225,7 +229,7 @@ export const authenticateBoxFlow = async ({ | RegisterNoSpace | { tag: "canceled" } > => { - const result2 = await registerFlow(registerFlowOpts); + const result2 = await registerFlow(registerFlowOpts); if (result2 === "canceled") { return { tag: "canceled" } as const; @@ -235,7 +239,7 @@ export const authenticateBoxFlow = async ({ return result2; } - result2 satisfies LoginSuccess; + result2 satisfies LoginSuccess; return { newAnchor: true, ...result2, @@ -255,7 +259,7 @@ export const authenticateBoxFlow = async ({ // Prompt for an identity number const doPrompt = async (): Promise< - | (LoginSuccess & { + | (LoginSuccess & { newAnchor: boolean; authnMethod: "pin" | "passkey" | "recovery"; }) @@ -284,7 +288,7 @@ export const authenticateBoxFlow = async ({ return { tag: "canceled" } as const; } - recoverResult satisfies LoginSuccess; + recoverResult satisfies LoginSuccess; return { newAnchor: false /* If an anchor was recovered, then it's _not_ a new anchor */, @@ -389,9 +393,11 @@ const clarifyError_ = ( ): Omit => clarifyError[flowError.kind](flowError); -export const handleLoginFlowResult = async ( - result: (LoginSuccess & E) | FlowError -): Promise<({ userNumber: bigint; connection: T } & E) | undefined> => { +export const handleLoginFlowResult = async ( + result: (LoginSuccess & E) | FlowError +): Promise< + ({ userNumber: bigint; connection: AuthenticatedConnection } & E) | undefined +> => { if (result.kind === "loginSuccess") { await setAnchorUsed(result.userNumber); return result; @@ -707,7 +713,7 @@ const pinIdentityToDerPubkey = async ( }; // Find and use a passkey, whether PIN or webauthn -const useIdentityFlow = async ({ +const useIdentityFlow = async ({ userNumber, allowPinAuthentication, retrievePinIdentityMaterial, @@ -724,7 +730,7 @@ const useIdentityFlow = async ({ loginPasskey: ( userNumber: bigint ) => Promise< - LoginSuccess | AuthFail | WebAuthnFailed | UnknownUser | ApiError + LoginSuccess | AuthFail | WebAuthnFailed | UnknownUser | ApiError >; allowPinAuthentication: boolean; verifyPinValidity: (opts: { @@ -739,9 +745,9 @@ const useIdentityFlow = async ({ userNumber: bigint; pin: string; pinIdentityMaterial: I; - }) => Promise | BadPin>; + }) => Promise; }): Promise< - | (LoginSuccess & { + | (LoginSuccess & { newAnchor: boolean; authnMethod: "pin" | "passkey" | "recovery"; }) @@ -792,7 +798,7 @@ const useIdentityFlow = async ({ } // Otherwise, attempt login with PIN - const result = await usePin | BadPin>({ + const result = await usePin({ verifyPin: async (pin) => { const result = await loginPinIdentityMaterial({ userNumber, @@ -804,7 +810,7 @@ const useIdentityFlow = async ({ return { ok: false, error: "Invalid PIN" }; } - result satisfies LoginSuccess; + result satisfies LoginSuccess; return { ok: true, value: result }; }, }); @@ -826,7 +832,7 @@ const useIdentityFlow = async ({ return { ...pinResult }; } - pinResult satisfies LoginSuccess; + pinResult satisfies LoginSuccess; // We log in with an existing PIN anchor, meaning it is _not_ a new anchor return { newAnchor: false, authnMethod: "pin", ...pinResult }; diff --git a/src/frontend/src/flows/authorize/index.ts b/src/frontend/src/flows/authorize/index.ts index 12586bf47d..9a6582fd2e 100644 --- a/src/frontend/src/flows/authorize/index.ts +++ b/src/frontend/src/flows/authorize/index.ts @@ -212,14 +212,17 @@ const authenticate = async ( const derivationOrigin = authContext.authRequest.derivationOrigin ?? authContext.requestOrigin; - // TODO: Commit state - const result = await withLoader(() => - fetchDelegation({ - connection: authSuccess.connection, - derivationOrigin, - publicKey: authContext.authRequest.sessionPublicKey, - maxTimeToLive: authContext.authRequest.maxTimeToLive, - }) + // Ignore the response of committing the metadata because it's not crucial. + const [result] = await withLoader(() => + Promise.all([ + fetchDelegation({ + connection: authSuccess.connection, + derivationOrigin, + publicKey: authContext.authRequest.sessionPublicKey, + maxTimeToLive: authContext.authRequest.maxTimeToLive, + }), + authSuccess.connection.commitMetadata(), + ]) ); if ("error" in result) { diff --git a/src/frontend/src/flows/manage/index.ts b/src/frontend/src/flows/manage/index.ts index c1f2ef3c44..33aeaed593 100644 --- a/src/frontend/src/flows/manage/index.ts +++ b/src/frontend/src/flows/manage/index.ts @@ -250,8 +250,10 @@ export const renderManage = async ({ for (;;) { let anchorInfo: IdentityAnchorInfo; try { - // TODO: commit state - anchorInfo = await withLoader(() => connection.getAnchorInfo()); + // Ignore the `commitMetadata` response, it's not critical for the application. + [anchorInfo] = await withLoader(() => + Promise.all([connection.getAnchorInfo(), connection.commitMetadata()]) + ); } catch (error: unknown) { await displayFailedToListDevices( error instanceof Error ? error : unknownError() @@ -367,13 +369,6 @@ export const displayManage = ( onAddDevice, addRecoveryPhrase, addRecoveryKey: async () => { - const confirmed = confirm( - "Add a Recovery Device\n\nUse a FIDO Security Key, like a YubiKey, as an additional recovery method." - ); - if (!confirmed) { - // No resolve here because we don't need to reload the screen - return; - } await setupKey({ connection }); resolve(); }, diff --git a/src/frontend/src/flows/recovery/recoveryWizard.ts b/src/frontend/src/flows/recovery/recoveryWizard.ts index b97dbd84b4..8fc214ad10 100644 --- a/src/frontend/src/flows/recovery/recoveryWizard.ts +++ b/src/frontend/src/flows/recovery/recoveryWizard.ts @@ -80,16 +80,31 @@ export const addPhrase = ({ ); }; +// TODO: Add e2e test https://dfinity.atlassian.net/browse/GIX-2600 export const recoveryWizard = async ( userNumber: bigint, connection: AuthenticatedConnection ): Promise => { // Here, if the user doesn't have any recovery device, we prompt them to add // one. - const recoveries = await withLoader(() => - connection.lookupRecovery(userNumber) + const [recoveries, identityMetadata] = await withLoader(() => + Promise.all([ + connection.lookupRecovery(userNumber), + connection.getIdentityMetadata(), + ]) ); - if (recoveries.length === 0) { + + const ONE_WEEK_MILLIS = 7 * 24 * 60 * 60 * 1000; + const nowInMillis = Date.now(); + const oneWeekAgoTimestamp = nowInMillis - ONE_WEEK_MILLIS; + const hasNotSeenRecoveryPageLastWeek = + (identityMetadata?.recoveryPageShownTimestampMillis ?? 0) < + oneWeekAgoTimestamp; + if (recoveries.length === 0 && hasNotSeenRecoveryPageLastWeek) { + // `await` here doesn't add any waiting time beacause we already got the metadata earlier. + await connection.updateIdentityMetadata({ + recoveryPageShownTimestampMillis: nowInMillis, + }); const doAdd = await addPhrase({ intent: "securityReminder" }); if (doAdd !== "cancel") { doAdd satisfies "ok"; diff --git a/src/frontend/src/flows/register/index.ts b/src/frontend/src/flows/register/index.ts index 3af07d85e7..ebba1b6472 100644 --- a/src/frontend/src/flows/register/index.ts +++ b/src/frontend/src/flows/register/index.ts @@ -20,7 +20,6 @@ import { authenticatorAttachmentToKeyType } from "$src/utils/authenticatorAttach import { ApiError, AuthFail, - AuthenticatedConnection, BadChallenge, Connection, IIWebAuthnIdentity, @@ -37,7 +36,7 @@ import { displayUserNumberWarmup } from "./finish"; import { savePasskeyOrPin } from "./passkey"; /** Registration (anchor creation) flow for new users */ -export const registerFlow = async ({ +export const registerFlow = async ({ createChallenge: createChallenge_, register, storePinIdentity, @@ -53,7 +52,7 @@ export const registerFlow = async ({ credentialId?: CredentialId; challengeResult: { chars: string; challenge: Challenge }; }) => Promise< - LoginSuccess | BadChallenge | ApiError | AuthFail | RegisterNoSpace + LoginSuccess | BadChallenge | ApiError | AuthFail | RegisterNoSpace >; storePinIdentity: (opts: { userNumber: bigint; @@ -63,7 +62,7 @@ export const registerFlow = async ({ pinAllowed: () => Promise; uaParser: PreloadedUAParser; }): Promise< - | (LoginSuccess & { authnMethod: "passkey" | "pin" }) + | (LoginSuccess & { authnMethod: "passkey" | "pin" }) | BadChallenge | ApiError | AuthFail @@ -193,6 +192,13 @@ export const registerFlow = async ({ result.kind satisfies "loginSuccess"; const userNumber = result.userNumber; await finalizeIdentity?.(userNumber); + // We don't want to nudge the user with the recovery phrase warning page + // right after they've created their anchor. + // The metadata starts to fetch when the connection is created. + // But it might not have finished yet, so we `await` for `updateIdentityMetadata` to also wait for it. + await result.connection.updateIdentityMetadata({ + recoveryPageShownTimestampMillis: Date.now(), + }); await setAnchorUsed(userNumber); await displayUserNumber({ userNumber, @@ -202,9 +208,7 @@ export const registerFlow = async ({ return { ...result, authnMethod }; }; -export type RegisterFlowOpts = Parameters< - typeof registerFlow ->[0]; +export type RegisterFlowOpts = Parameters[0]; export const getRegisterFlowOpts = ({ connection, diff --git a/src/frontend/src/repositories/identityMetadata.ts b/src/frontend/src/repositories/identityMetadata.ts index f538edb55f..9bc40199e3 100644 --- a/src/frontend/src/repositories/identityMetadata.ts +++ b/src/frontend/src/repositories/identityMetadata.ts @@ -101,9 +101,9 @@ export class IdentityMetadataRepository { metadata: RawMetadataState ): metadata is MetadataMapV2 => { return ( - this.rawMetadata !== "loading" && - this.rawMetadata !== "error" && - this.rawMetadata !== "not-loaded" + metadata !== "loading" && + metadata !== "error" && + metadata !== "not-loaded" ); }; diff --git a/src/frontend/src/test-e2e/alternativeOrigin/endpointFormat.test.ts b/src/frontend/src/test-e2e/alternativeOrigin/endpointFormat.test.ts index 5ddae32772..442145eae8 100644 --- a/src/frontend/src/test-e2e/alternativeOrigin/endpointFormat.test.ts +++ b/src/frontend/src/test-e2e/alternativeOrigin/endpointFormat.test.ts @@ -21,7 +21,6 @@ test("Should not issue delegation when /.well-known/ii-alternative-origins has t const authenticatorId1 = await addVirtualAuthenticator(browser); await browser.url(II_URL); await FLOWS.registerNewIdentityWelcomeView(browser); - await FLOWS.addRecoveryMechanismSeedPhrase(browser); const credentials = await getWebAuthnCredentials(browser, authenticatorId1); expect(credentials).toHaveLength(1); @@ -59,7 +58,6 @@ test("Should not follow redirect returned by /.well-known/ii-alternative-origins const authenticatorId1 = await addVirtualAuthenticator(browser); await browser.url(II_URL); await FLOWS.registerNewIdentityWelcomeView(browser); - await FLOWS.addRecoveryMechanismSeedPhrase(browser); const credentials = await getWebAuthnCredentials(browser, authenticatorId1); expect(credentials).toHaveLength(1); @@ -97,7 +95,6 @@ test("Should fetch /.well-known/ii-alternative-origins using the non-raw url", a const authenticatorId1 = await addVirtualAuthenticator(browser); await browser.url(II_URL); const userNumber = await FLOWS.registerNewIdentityWelcomeView(browser); - await FLOWS.addRecoveryMechanismSeedPhrase(browser); const credentials = await getWebAuthnCredentials(browser, authenticatorId1); expect(credentials).toHaveLength(1); @@ -142,7 +139,6 @@ test("Should allow arbitrary URL as derivation origin", async () => { const authenticatorId1 = await addVirtualAuthenticator(browser); await browser.url(II_URL); const userNumber = await FLOWS.registerNewIdentityWelcomeView(browser); - await FLOWS.addRecoveryMechanismSeedPhrase(browser); const credentials = await getWebAuthnCredentials(browser, authenticatorId1); expect(credentials).toHaveLength(1); diff --git a/src/frontend/src/test-e2e/alternativeOrigin/ingressFormat.test.ts b/src/frontend/src/test-e2e/alternativeOrigin/ingressFormat.test.ts index edff76990a..e4640806b3 100644 --- a/src/frontend/src/test-e2e/alternativeOrigin/ingressFormat.test.ts +++ b/src/frontend/src/test-e2e/alternativeOrigin/ingressFormat.test.ts @@ -20,7 +20,6 @@ test("Should not issue delegation when derivationOrigin is missing from /.well-k const authenticatorId1 = await addVirtualAuthenticator(browser); await browser.url(II_URL); const _userNumber = await FLOWS.registerNewIdentityWelcomeView(browser); - await FLOWS.addRecoveryMechanismSeedPhrase(browser); const credentials = await getWebAuthnCredentials(browser, authenticatorId1); expect(credentials).toHaveLength(1); diff --git a/src/frontend/src/test-e2e/flows.ts b/src/frontend/src/test-e2e/flows.ts index 93c6302872..166562458f 100644 --- a/src/frontend/src/test-e2e/flows.ts +++ b/src/frontend/src/test-e2e/flows.ts @@ -80,16 +80,11 @@ export const FLOWS = { }, loginWelcomeView: async ( userNumber: string, - deviceName: string, browser: WebdriverIO.Browser ): Promise => { const welcomeView = new WelcomeView(browser); await welcomeView.waitForDisplay(); await welcomeView.login(userNumber); - // This flow assumes no recovery phrase, so we explicitly skip the recovery nag here - await FLOWS.skipRecoveryNag(browser); - const mainView = new MainView(browser); - await mainView.waitForDeviceDisplay(deviceName); }, loginAuthenticateView: async ( userNumber: string, @@ -99,8 +94,6 @@ export const FLOWS = { const authenticateView = new AuthenticateView(browser); await authenticateView.waitForDisplay(); await authenticateView.pickAnchor(userNumber); - // This flow assumes no recovery phrase, so we explicitly skip the recovery nag here - await FLOWS.skipRecoveryNag(browser); const mainView = new MainView(browser); await mainView.waitForDeviceDisplay(deviceName); }, @@ -115,8 +108,6 @@ export const FLOWS = { const pinAuthView = new PinAuthView(browser); await pinAuthView.waitForDisplay(); await pinAuthView.enterPin(pin); - // This flow assumes no recovery phrase, so we explicitly skip the recovery nag here - await FLOWS.skipRecoveryNag(browser); }, loginPinWelcomeView: async ( userNumber: string, @@ -129,8 +120,6 @@ export const FLOWS = { const pinAuthView = new PinAuthView(browser); await pinAuthView.waitForDisplay(); await pinAuthView.enterPin(pin); - // This flow assumes no recovery phrase, so we explicitly skip the recovery nag here - await FLOWS.skipRecoveryNag(browser); }, addRecoveryMechanismSeedPhrase: async ( browser: WebdriverIO.Browser @@ -178,11 +167,6 @@ export const FLOWS = { await addDeviceSuccessView.waitForDisplay(); await addDeviceSuccessView.continue(); }, - skipRecoveryNag: async (browser: WebdriverIO.Browser): Promise => { - const recoveryMethodSelectorView = new RecoveryMethodSelectorView(browser); - await recoveryMethodSelectorView.waitForDisplay(); - await recoveryMethodSelectorView.skipRecovery(); - }, recoverUsingSeedPhrase: async ( browser: WebdriverIO.Browser, recoveryPhrase: string diff --git a/src/frontend/src/test-e2e/pinAuth.test.ts b/src/frontend/src/test-e2e/pinAuth.test.ts index 966b7b4b61..96edddbd0c 100644 --- a/src/frontend/src/test-e2e/pinAuth.test.ts +++ b/src/frontend/src/test-e2e/pinAuth.test.ts @@ -93,8 +93,6 @@ test("Register and log in with PIN identity, retry on wrong PIN", async () => { await pinAuthView.waitForError(); await pinAuthView.enterPin(pin); - // NOTE: handle recovery nag because there is no recovery phrase - await FLOWS.skipRecoveryNag(browser); const mainView2 = new MainView(browser); await mainView2.waitForDisplay(); // we should be logged in }, APPLE_USER_AGENT); diff --git a/src/frontend/src/test-e2e/principals.test.ts b/src/frontend/src/test-e2e/principals.test.ts index 05dca02dd6..cb1f4af0fa 100644 --- a/src/frontend/src/test-e2e/principals.test.ts +++ b/src/frontend/src/test-e2e/principals.test.ts @@ -21,7 +21,6 @@ test("Should issue the same principal to nice url and canonical url", async () = const authenticatorId1 = await addVirtualAuthenticator(browser); await browser.url(II_URL); const userNumber = await FLOWS.registerNewIdentityWelcomeView(browser); - await FLOWS.addRecoveryMechanismSeedPhrase(browser); const credentials = await getWebAuthnCredentials(browser, authenticatorId1); expect(credentials).toHaveLength(1); @@ -78,7 +77,6 @@ test("Should issue the same principal to dapps on legacy & official domains", as const registrationAuthenticator = await addVirtualAuthenticator(browser); await browser.url(II_URL); const userNumber = await FLOWS.registerNewIdentityWelcomeView(browser); - await FLOWS.addRecoveryMechanismSeedPhrase(browser); // avoids being prompted later during authz const credentials = await getWebAuthnCredentials( browser, registrationAuthenticator diff --git a/src/frontend/src/test-e2e/register.test.ts b/src/frontend/src/test-e2e/register.test.ts index 2b8fcba2f7..66ab7392d1 100644 --- a/src/frontend/src/test-e2e/register.test.ts +++ b/src/frontend/src/test-e2e/register.test.ts @@ -39,7 +39,8 @@ test("Register new identity and login without prefilled identity number", async // load the II page again await browser.url(II_URL); - await FLOWS.loginWelcomeView(userNumber, DEVICE_NAME1, browser); + await FLOWS.loginWelcomeView(userNumber, browser); + await mainView.waitForDeviceDisplay(DEVICE_NAME1); }); }, 300_000); @@ -92,7 +93,6 @@ test("Register first then log into client application", async () => { const authenticateView = new AuthenticateView(browser); await authenticateView.waitForDisplay(); await authenticateView.pickAnchor(userNumber); - await FLOWS.skipRecoveryNag(browser); const principal = await demoAppView.waitForAuthenticated(); expect(await demoAppView.whoami()).toBe(principal); diff --git a/src/frontend/src/test-e2e/verifiableCredentials/utils.ts b/src/frontend/src/test-e2e/verifiableCredentials/utils.ts index 2bbbd4d093..18506fe786 100644 --- a/src/frontend/src/test-e2e/verifiableCredentials/utils.ts +++ b/src/frontend/src/test-e2e/verifiableCredentials/utils.ts @@ -198,7 +198,6 @@ export const register: Record< const authenticatorId = await addVirtualAuthenticator(browser); const userNumber = await FLOWS.registerNewIdentityWelcomeView(browser); const credentials = await getWebAuthnCredentials(browser, authenticatorId); - await FLOWS.addRecoveryMechanismSeedPhrase(browser); return { userNumber, @@ -220,7 +219,6 @@ export const register: Record< pin: async (browser: WebdriverIO.Browser) => { const pin = "123456"; const userNumber = await FLOWS.registerPinWelcomeView(browser, pin); - await FLOWS.addRecoveryMechanismSeedPhrase(browser); return { userNumber, diff --git a/src/frontend/src/utils/iiConnection.ts b/src/frontend/src/utils/iiConnection.ts index 237aefe46f..c938ac2846 100644 --- a/src/frontend/src/utils/iiConnection.ts +++ b/src/frontend/src/utils/iiConnection.ts @@ -84,9 +84,9 @@ export class DummyIdentity export const IC_DERIVATION_PATH = [44, 223, 0, 0, 0]; -export type LoginSuccess = { +export type LoginSuccess = { kind: "loginSuccess"; - connection: T; + connection: AuthenticatedConnection; userNumber: bigint; }; diff --git a/src/showcase/src/flows.ts b/src/showcase/src/flows.ts index 0897ee49ac..f2af4fe472 100644 --- a/src/showcase/src/flows.ts +++ b/src/showcase/src/flows.ts @@ -1,7 +1,12 @@ +import { _SERVICE } from "$generated/internet_identity_types"; import { authenticateBoxFlow } from "$src/components/authenticateBox"; import { withLoader } from "$src/components/loader"; import { toast } from "$src/components/toast"; import { registerFlow, RegisterFlowOpts } from "$src/flows/register"; +import { AuthenticatedConnection } from "$src/utils/iiConnection"; +import { MultiWebAuthnIdentity } from "$src/utils/multiWebAuthnIdentity"; +import { ActorSubclass } from "@dfinity/agent"; +import { DelegationIdentity } from "@dfinity/identity"; import { html, render, TemplateResult } from "lit-html"; import { dummyChallenge } from "./constants"; import { i18n } from "./i18n"; @@ -26,7 +31,37 @@ export const flowsPage = () => { render(pageContent, container); }; -const registerFlowOpts: RegisterFlowOpts = { +const mockDelegationIdentity = {} as DelegationIdentity; +const mockActor = { + identity_info: () => { + return { + Ok: { + authn_methods: [], + metadata: [], + authn_method_registration: [], + }, + }; + }, +} as unknown as ActorSubclass<_SERVICE>; + +class MockAuthenticatedConnection extends AuthenticatedConnection { + constructor() { + super( + "12345", + MultiWebAuthnIdentity.fromCredentials([]), + mockDelegationIdentity, + BigInt(12345), + mockActor + ); + } + setShownRecoveryWarningPage = async (): Promise => { + // Do nothing + }; +} + +const mockConnection = new MockAuthenticatedConnection(); + +const registerFlowOpts: RegisterFlowOpts = { createChallenge: async () => { await new Promise((resolve) => setTimeout(resolve, 2000)); return dummyChallenge; @@ -40,7 +75,7 @@ const registerFlowOpts: RegisterFlowOpts = { return { kind: "loginSuccess", userNumber: BigInt(12356), - connection: null, + connection: mockConnection, }; }, registrationAllowed: true, @@ -54,7 +89,7 @@ const registerFlowOpts: RegisterFlowOpts = { export const iiFlows: Record void> = { loginManage: async () => { - const result = await authenticateBoxFlow({ + const result = await authenticateBoxFlow<"identity">({ i18n, templates: manageTemplates, addDevice: () => { @@ -70,7 +105,7 @@ export const iiFlows: Record void> = { return Promise.resolve({ kind: "loginSuccess", userNumber: BigInt(1234), - connection: null, + connection: mockConnection, }); }, allowPinAuthentication: true, @@ -86,7 +121,7 @@ export const iiFlows: Record void> = { return Promise.resolve({ kind: "loginSuccess", userNumber: BigInt(1234), - connection: null, + connection: mockConnection, }); }, recover: () => { @@ -94,7 +129,7 @@ export const iiFlows: Record void> = { return Promise.resolve({ kind: "loginSuccess", userNumber: BigInt(1234), - connection: null, + connection: mockConnection, }); }, retrievePinIdentityMaterial: ({ userNumber }) => { @@ -129,11 +164,11 @@ export const iiFlows: Record void> = { `); }, register: async () => { - const result = await registerFlow(registerFlowOpts); + const result = await registerFlow(registerFlowOpts); toast.success(registerSuccessToastTemplate(result)); }, registerWithPin: async () => { - const result = await registerFlow({ + const result = await registerFlow({ ...registerFlowOpts, pinAllowed: () => Promise.resolve(true), });