Skip to content

Commit 15246bb

Browse files
authored
Implement links to recovery set-up and identity recovery pages. (#3539)
Implement links to recovery set-up and identity recovery pages. # Changes - Implement tabs styling. - Move upgrade footer in auth flow to continue with passkey view. - Add recover footer to access method selection view in auth flow.
1 parent 0562023 commit 15246bb

File tree

15 files changed

+108
-70
lines changed

15 files changed

+108
-70
lines changed

src/frontend/src/lib/app.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@import "tailwindcss" source("../");
22
@import "./styles/fixes.css";
33
@import "./styles/button.css";
4+
@import "./styles/tabs.css";
45

56
@plugin "@tailwindcss/forms";
67

src/frontend/src/lib/components/wizards/auth/AuthWizard.svelte

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
const authFlow = new AuthFlow();
3939
4040
let isContinueFromAnotherDeviceVisible = $state(false);
41-
let isMigrating = $state(false);
41+
let isUpgrading = $state(false);
4242
4343
const handleContinueWithExistingPasskey = async (): Promise<
4444
void | "cancelled"
@@ -115,6 +115,7 @@
115115
<SetupOrUseExistingPasskey
116116
setupNew={authFlow.setupNewPasskey}
117117
useExisting={handleContinueWithExistingPasskey}
118+
upgrade={() => (isUpgrading = true)}
118119
/>
119120
{:else if authFlow.view === "setupNewPasskey"}
120121
<CreatePasskey create={handleCreatePasskey} />
@@ -131,9 +132,9 @@
131132
{:else}
132133
<RegisterAccessMethodWizard onRegistered={handleRegistered} {onError} />
133134
{/if}
134-
{:else if isMigrating}
135+
{:else if isUpgrading}
135136
{#if !withinDialog}
136-
<Dialog onClose={() => (isMigrating = false)}>
137+
<Dialog onClose={() => (isUpgrading = false)}>
137138
<MigrationWizard onSuccess={onMigration} {onError} />
138139
</Dialog>
139140
{:else}
@@ -147,7 +148,6 @@
147148
<PickAuthenticationMethod
148149
setupOrUseExistingPasskey={authFlow.setupOrUseExistingPasskey}
149150
continueWithOpenId={handleContinueWithOpenId}
150-
migrate={() => (isMigrating = true)}
151151
/>
152152
{/if}
153153
{#if authFlow.view !== "chooseMethod"}

src/frontend/src/lib/components/wizards/auth/views/PickAuthenticationMethod.svelte

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,9 @@
1414
interface Props {
1515
setupOrUseExistingPasskey: () => void;
1616
continueWithOpenId: (config: OpenIdConfig) => Promise<void | "cancelled">;
17-
migrate: () => void;
1817
}
1918
20-
const { setupOrUseExistingPasskey, continueWithOpenId, migrate }: Props =
21-
$props();
19+
const { setupOrUseExistingPasskey, continueWithOpenId }: Props = $props();
2220
2321
let authenticatingProviderId = $state<string | null>(null);
2422
let cancelledProviderId = $state<string | null>(null);
@@ -162,13 +160,14 @@
162160
{/if}
163161
<div class="flex flex-row items-center justify-between gap-4">
164162
<p class="text-text-secondary text-sm">
165-
{$t`Still have an identity number?`}
163+
{$t`Lost access to your identity?`}
166164
</p>
167-
<button
168-
onclick={migrate}
165+
<a
166+
href="/recovery"
167+
target="_blank"
169168
class="text-text-primary text-sm font-semibold outline-0 hover:underline focus-visible:underline"
170169
>
171-
{$t`Upgrade`}
172-
</button>
170+
{$t`Recover`}
171+
</a>
173172
</div>
174173
</div>

src/frontend/src/lib/components/wizards/auth/views/SetupOrUseExistingPasskey.svelte

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
interface Props {
1414
setupNew: () => void;
1515
useExisting: () => Promise<void | "cancelled">;
16+
upgrade: () => void;
1617
}
1718
18-
const { setupNew, useExisting }: Props = $props();
19+
const { setupNew, useExisting, upgrade }: Props = $props();
1920
2021
let isAuthenticating = $state(false);
2122
let isCancelled = $state(false);
@@ -71,34 +72,16 @@
7172
{/if}
7273
</Button>
7374
</Tooltip>
74-
<div class="mt-3 flex flex-row items-center">
75+
<div class="border-border-tertiary my-5 border-t"></div>
76+
<div class="flex flex-row items-center justify-between gap-4">
7577
<p class="text-text-secondary text-sm">
76-
<Trans>
77-
<a
78-
href={II_SUPPORT_PRIVACY_SECURITY}
79-
target="_blank"
80-
class="text-text-primary font-semibold hover:underline"
81-
>
82-
Learn more
83-
</a>
84-
about privacy preservation
85-
</Trans>
78+
{$t`Still have an identity number?`}
8679
</p>
87-
<Tooltip
88-
label={$t`Internet Identity protects your privacy`}
89-
description={$t`Your personal data, such as your name or email, is never shared. Each app gets a unique pseudonym to prevent tracking automatically.`}
90-
direction="up"
91-
align="end"
92-
offset="0rem"
93-
class="max-w-80"
80+
<button
81+
onclick={upgrade}
82+
class="text-text-primary text-sm font-semibold outline-0 hover:underline focus-visible:underline"
9483
>
95-
<Button
96-
variant="tertiary"
97-
iconOnly
98-
class="ms-auto !cursor-default !rounded-full"
99-
>
100-
<HelpCircleIcon class="size-5" />
101-
</Button>
102-
</Tooltip>
84+
{$t`Upgrade`}
85+
</button>
10386
</div>
10487
</div>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
@utility tabs {
2+
@apply relative flex flex-row gap-4;
3+
@apply before:border-border-secondary before:absolute before:inset-x-0 before:bottom-0 before:border-b-1;
4+
}
5+
6+
@utility tab {
7+
@apply flex flex-row items-center gap-2 pb-3;
8+
@apply text-text-tertiary text-sm font-semibold;
9+
@apply border-b-2 border-transparent;
10+
}
11+
12+
@utility tab-selected {
13+
@apply border-fg-primary;
14+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<script lang="ts">
2+
import type { LayoutProps } from "./$types";
3+
import { t } from "$lib/stores/locale.store";
4+
import Badge from "$lib/components/ui/Badge.svelte";
5+
import { page } from "$app/state";
6+
import { toAccessMethods } from "./access/utils";
7+
8+
const { children, data }: LayoutProps = $props();
9+
10+
const accessMethodCount = $derived(toAccessMethods(data.identityInfo).length);
11+
</script>
12+
13+
<nav class="tabs mb-8">
14+
<a
15+
href="/manage/access"
16+
class={["tab", page.url.pathname === "/manage/access" && "tab-selected"]}
17+
>
18+
<span>{$t`Access methods`}</span>
19+
<Badge size="sm">{accessMethodCount}</Badge>
20+
</a>
21+
<a
22+
href="/manage/recovery"
23+
class={["tab", page.url.pathname === "/manage/recovery" && "tab-selected"]}
24+
>
25+
{$t`Recovery phrase`}
26+
</a>
27+
</nav>
28+
29+
{@render children()}

src/frontend/src/routes/(new-styling)/manage/(authenticated)/+layout.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,11 @@
145145
<li class="contents">
146146
<NavItem
147147
href="/manage/access"
148-
current={page.url.pathname === "/manage/access"}
148+
current={page.url.pathname === "/manage/access" ||
149+
page.url.pathname === "/manage/recovery"}
149150
>
150151
<KeyRoundIcon class="size-5 sm:max-md:mx-auto" />
151-
<span class="sm:max-md:hidden">{$t`Access methods`}</span>
152+
<span class="sm:max-md:hidden">{$t`Access and recovery`}</span>
152153
</NavItem>
153154
</li>
154155
</ul>

src/frontend/tests/e2e-playwright/authorize/index.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ test("Authorize by signing in from another device", async ({
135135
if (await menuButton.isVisible()) {
136136
await menuButton.click();
137137
}
138-
await otherDevicePage.getByRole("link", { name: "Access methods" }).click();
138+
await otherDevicePage
139+
.getByRole("link", { name: "Access and recovery" })
140+
.click();
139141

140142
// Verify we have two passkeys
141143
await expect(otherDevicePage.getByText("Chrome")).toHaveCount(2);

src/frontend/tests/e2e-playwright/authorize/migration.spec.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,23 +54,26 @@ test.describe("Migration from an app", () => {
5454
throw new Error("Credential or identity number not found");
5555
}
5656
// Step 3: Perform the migration
57-
await authPage.getByRole("button", { name: "Upgrade" }).click();
57+
await authPage
58+
.getByRole("button", { name: "Continue with passkey" })
59+
.click();
60+
const dialog = authPage.getByRole("dialog");
61+
await expect(dialog).toBeVisible();
62+
await dialog.getByRole("button", { name: "Upgrade" }).click();
5863
const authAuthenticatorId = await addVirtualAuthenticator(authPage);
5964
await addCredentialToVirtualAuthenticator(
6065
authPage,
6166
authAuthenticatorId,
6267
credential,
6368
);
64-
await authPage
69+
await dialog
6570
.getByPlaceholder("Internet Identity number")
6671
.fill(identityNumber);
67-
await authPage.getByRole("button", { name: "Continue" }).click();
72+
await dialog.getByRole("button", { name: "Continue" }).click();
6873

69-
await authPage.getByLabel("Identity name").fill(TEST_USER_NAME);
74+
await dialog.getByLabel("Identity name").fill(TEST_USER_NAME);
7075
auth(authPage);
71-
await authPage
72-
.getByRole("button", { name: "Upgrade identity" })
73-
.click();
76+
await dialog.getByRole("button", { name: "Upgrade identity" }).click();
7477
},
7578
);
7679
expect(legacyPrincipal).toEqual(migratedPrincipal);

src/frontend/tests/e2e-playwright/dashboard/addPasskeys.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ test("User can log into the dashboard and add a new passkey from the same device
3535
if (await menuButton.isVisible()) {
3636
await menuButton.click();
3737
}
38-
await page.getByRole("link", { name: "Access methods" }).click();
38+
await page.getByRole("link", { name: "Access and recovery" }).click();
3939

4040
// Verify we have one passkey and rename it
4141
await expect(
@@ -84,7 +84,7 @@ test("User can log into the dashboard and add a new passkey from the same device
8484
if (await newMenuButton.isVisible()) {
8585
await newMenuButton.click();
8686
}
87-
await newPage.getByRole("link", { name: "Access methods" }).click();
87+
await newPage.getByRole("link", { name: "Access and recovery" }).click();
8888

8989
// Verify that new passkey is the one currently in use
9090
await expect(
@@ -126,7 +126,7 @@ test("User can log in the dashboard and add a new passkey from another device",
126126
if (await menuButton.isVisible()) {
127127
await menuButton.click();
128128
}
129-
await page.getByRole("link", { name: "Access methods" }).click();
129+
await page.getByRole("link", { name: "Access and recovery" }).click();
130130

131131
// Verify we have one passkey
132132
await expect(page.getByText("Chrome")).toHaveCount(1);
@@ -211,7 +211,7 @@ test("User can add a new passkey and use it with cached identity without clearin
211211
if (await menuButton.isVisible()) {
212212
await menuButton.click();
213213
}
214-
await page.getByRole("link", { name: "Access methods" }).click();
214+
await page.getByRole("link", { name: "Access and recovery" }).click();
215215

216216
// Verify we have one passkey and rename it
217217
await expect(
@@ -290,7 +290,7 @@ test("User can log into the dashboard and add up to 7 additional passkeys", asyn
290290
if (await menuButton.isVisible()) {
291291
await menuButton.click();
292292
}
293-
await page.getByRole("link", { name: "Access methods" }).click();
293+
await page.getByRole("link", { name: "Access and recovery" }).click();
294294

295295
// Verify we have one passkey
296296
await expect(

0 commit comments

Comments
 (0)