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);
+};