diff --git a/src/frontend/src/banner.ts b/src/frontend/src/banner.ts index fbcdf467a2..db42a67373 100644 --- a/src/frontend/src/banner.ts +++ b/src/frontend/src/banner.ts @@ -29,7 +29,7 @@ export const showWarningIfNecessary = (): void => { } }; -export const showWarning = (message: TemplateResult): void => { +export const showWarning = (message: TemplateResult): HTMLDivElement => { const container = document.createElement("div"); container.className = "features-warning-container"; container.setAttribute("role", "alert"); @@ -72,4 +72,5 @@ export const showWarning = (message: TemplateResult): void => { render(warning, container); document.body.prepend(container); + return container; }; diff --git a/src/frontend/src/flows/manage/index.ts b/src/frontend/src/flows/manage/index.ts index dc2646ca9d..ff9df3e6ab 100644 --- a/src/frontend/src/flows/manage/index.ts +++ b/src/frontend/src/flows/manage/index.ts @@ -1,9 +1,11 @@ import { TemplateResult, render, html } from "lit-html"; +import { LEGACY_II_URL } from "../../config"; import { Connection, AuthenticatedConnection } from "../../utils/iiConnection"; import { withLoader } from "../../components/loader"; import { unreachable } from "../../utils/utils"; import { logoutSection } from "../../components/logout"; import { deviceSettings } from "./deviceSettings"; +import { showWarning } from "../../banner"; import { DeviceData, IdentityAnchorInfo, @@ -14,7 +16,7 @@ import { authenticateBox, AuthnTemplates, } from "../../components/authenticateBox"; -import { setupRecovery } from "../recovery/setupRecovery"; +import { setupRecovery, setupPhrase } from "../recovery/setupRecovery"; import { recoveryWizard } from "../recovery/recoveryWizard"; import { pollForTentativeDevice } from "../addDevice/manage/pollForTentativeDevice"; import { chooseDeviceAddFlow } from "../addDevice/manage"; @@ -304,6 +306,24 @@ export const displayManage = ( renderManage(userNumber, connection); } ); + + // When visiting the legacy URL (ic0.app) we extra-nudge the users to create a recovery phrase, + // if they don't have one already. We lead them straight to recovery phrase creation, because + // recovery _device_ would be tied to the domain (which we want to avoid). + if (window.location.origin === LEGACY_II_URL && !hasRecoveryPhrase(devices)) { + const elem = showWarning(html`Important! + Create a recovery phrase. + `); + } render(template, container); renderDevices(userNumber, connection, devices); }; @@ -365,6 +385,10 @@ const renderDevices = async ( const hasRecoveryDevice = (devices: DeviceData[]): boolean => devices.some((device) => "recovery" in device.purpose); +// Whether the user has a recovery phrase or not +const hasRecoveryPhrase = (devices: DeviceData[]): boolean => + devices.some((device) => device.alias === "Recovery phrase"); + const unknownError = (): Error => { return new Error("Unknown error"); }; diff --git a/src/frontend/src/flows/recovery/setupRecovery.ts b/src/frontend/src/flows/recovery/setupRecovery.ts index 6896bc37c9..c00d7d6874 100644 --- a/src/frontend/src/flows/recovery/setupRecovery.ts +++ b/src/frontend/src/flows/recovery/setupRecovery.ts @@ -54,22 +54,7 @@ export const setupRecovery = async ( ); } case "seedPhrase": { - const name = "Recovery phrase"; - const seedPhrase = generate().trim(); - const recoverIdentity = await fromMnemonicWithoutValidation( - seedPhrase, - IC_DERIVATION_PATH - ); - await withLoader(() => - connection.add( - name, - { seed_phrase: null }, - { recovery: null }, - recoverIdentity.getPublicKey().toDer(), - { unprotected: null } - ) - ); - await displaySeedPhrase(userNumber.toString(10) + " " + seedPhrase); + await setupPhrase(userNumber, connection); break; } } @@ -82,3 +67,25 @@ export const setupRecovery = async ( }); } }; + +export const setupPhrase = async ( + userNumber: bigint, + connection: AuthenticatedConnection +) => { + const name = "Recovery phrase"; + const seedPhrase = generate().trim(); + const recoverIdentity = await fromMnemonicWithoutValidation( + seedPhrase, + IC_DERIVATION_PATH + ); + await withLoader(() => + connection.add( + name, + { seed_phrase: null }, + { recovery: null }, + recoverIdentity.getPublicKey().toDer(), + { unprotected: null } + ) + ); + await displaySeedPhrase(userNumber.toString(10) + " " + seedPhrase); +};