diff --git a/src/frontend/src/flows/manage/index.ts b/src/frontend/src/flows/manage/index.ts
index 6466d3bda2..d90265b765 100644
--- a/src/frontend/src/flows/manage/index.ts
+++ b/src/frontend/src/flows/manage/index.ts
@@ -2,7 +2,7 @@ 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 { unreachable, unknownToString } from "../../utils/utils";
import { logoutSection } from "../../components/logout";
import { deviceSettings } from "./deviceSettings";
import { showWarning } from "../../banner";
@@ -28,6 +28,16 @@ import {
recoveryDeviceToLabel,
} from "../../utils/recoveryDevice";
+// A simple representation of "device"s used on the manage page.
+export type Device = {
+ // Open the settings screen for that particular device
+ openSettings: () => Promise;
+ // The displayed name of a device (not exactly the "alias") because
+ // recovery devices handle aliases differently.
+ label: string;
+ isRecovery: boolean;
+};
+
/* Template for the authbox when authenticating to II */
export const authnTemplateManage = (): AuthnTemplates => {
const wrap = ({
@@ -94,8 +104,6 @@ const displayFailedToListDevices = (error: Error) =>
// and we (the frontend) only allow user one recovery device per type (phrase, fob),
// which leaves room for 8 authenticator devices.
const MAX_AUTHENTICATORS = 8;
-const numAuthenticators = (devices: DeviceData[]) =>
- devices.filter((device) => "authentication" in device.purpose).length;
// Actual page content. We display the Identity Anchor and the list of
// (non-recovery) devices. Additionally, if the user does _not_ have any
@@ -105,12 +113,14 @@ const numAuthenticators = (devices: DeviceData[]) =>
// recovery devices.
const pageContent = ({
userNumber,
- devices,
+ authenticators,
+ recoveries,
onAddDevice,
onAddRecovery,
}: {
userNumber: bigint;
- devices: DeviceData[];
+ authenticators: Device[];
+ recoveries: Device[];
onAddDevice: (next: "canceled" | "local" | "remote") => void;
onAddRecovery: () => void;
}): TemplateResult => {
@@ -121,9 +131,10 @@ const pageContent = ({
Add devices and recovery methods to make your anchor more secure.
- ${anchorSection(userNumber)} ${devicesSection(devices, onAddDevice)}
- ${!hasRecoveryDevice(devices) ? recoveryNag({ onAddRecovery }) : undefined}
- ${recoverySection(devices, onAddRecovery)} ${logoutSection()}
+ ${anchorSection(userNumber)}
+ ${devicesSection({ authenticators, onAddDevice })}
+ ${recoveries.length === 0 ? recoveryNag({ onAddRecovery }) : undefined}
+ ${recoverySection({ recoveries, onAddRecovery })} ${logoutSection()}
`;
return mainWindow({
@@ -143,12 +154,16 @@ const anchorSection = (userNumber: bigint): TemplateResult => html`
`;
-const devicesSection = (
- devices: DeviceData[],
- onAddDevice: (next: "canceled" | "local" | "remote") => void
-): TemplateResult => {
+// The regular, "authenticator" devices
+const devicesSection = ({
+ authenticators,
+ onAddDevice,
+}: {
+ authenticators: Device[];
+ onAddDevice: (next: "canceled" | "local" | "remote") => void;
+}): TemplateResult => {
const wrapClasses = ["l-stack"];
- const isWarning = devices.length < 2;
+ const isWarning = authenticators.length < 2;
if (isWarning === true) {
wrapClasses.push("c-card", "c-card--narrow", "c-card--warning");
@@ -170,7 +185,7 @@ const devicesSection = (
You can register up to ${MAX_AUTHENTICATORS} authenticator
devices (recovery devices excluded)
- (${numAuthenticators(devices)}/${MAX_AUTHENTICATORS})
+ (${authenticators.length}/${MAX_AUTHENTICATORS})
@@ -184,10 +199,21 @@ const devicesSection = (
}
-
+
+
+ ${authenticators.map((device) => {
+ return html`
+ -
+ ${deviceListItem({
+ device,
+ })}
+
+ `;
+ })}
+
-