Skip to content

Commit 4d48829

Browse files
authored
fix(eid-wallet): mask security-answer input with reveal toggle (#1042)
* fix(eid-wallet): mask security-answer input with reveal toggle * mask answer by default when returning to Restore answer step
1 parent 8890fad commit 4d48829

2 files changed

Lines changed: 76 additions & 24 deletions

File tree

infrastructure/eid-wallet/src/routes/(app)/personal/components/AddKnowledgeSheet.svelte

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
import { ButtonAction } from "$lib/ui";
33
import BottomSheet from "$lib/ui/BottomSheet/BottomSheet.svelte";
44
import { PERSONAL_BINDING_MAX_LENGTH } from "$lib/utils/personalBinding";
5-
import { Cancel01Icon } from "@hugeicons/core-free-icons";
5+
import {
6+
Cancel01Icon,
7+
ViewIcon,
8+
ViewOffIcon,
9+
} from "@hugeicons/core-free-icons";
610
import { HugeiconsIcon } from "@hugeicons/svelte";
711
812
interface IAddKnowledgeSheetProps {
@@ -19,13 +23,17 @@ let {
1923
2024
let question = $state("");
2125
let answer = $state("");
26+
// The answer is sensitive — mask it by default, with an eye toggle to reveal
27+
// (the spelling tip above means the user needs to be able to check it).
28+
let showAnswer = $state(false);
2229
2330
// On open: seed the question from the prop. Answer always starts blank —
2431
// we never round-trip the raw answer, so editing means re-entering it.
2532
$effect(() => {
2633
if (!isOpen) return;
2734
question = currentQuestion;
2835
answer = "";
36+
showAnswer = false;
2937
});
3038
3139
const canSave = $derived(
@@ -85,17 +93,35 @@ function close() {
8593
<label for="kn-answer" class="block text-black-500 mb-2">
8694
Answer
8795
</label>
88-
<input
89-
id="kn-answer"
90-
type="text"
91-
autocomplete="off"
92-
autocorrect="off"
93-
autocapitalize="off"
94-
spellcheck="false"
95-
bind:value={answer}
96-
maxlength={PERSONAL_BINDING_MAX_LENGTH}
97-
class="w-full bg-card-alternative rounded-full px-5 py-4 placeholder:text-black-300 outline-none focus:ring-2 focus:ring-primary"
98-
/>
96+
<div class="relative">
97+
<input
98+
id="kn-answer"
99+
type={showAnswer ? "text" : "password"}
100+
autocomplete="off"
101+
autocorrect="off"
102+
autocapitalize="off"
103+
spellcheck="false"
104+
bind:value={answer}
105+
maxlength={PERSONAL_BINDING_MAX_LENGTH}
106+
class="w-full bg-card-alternative rounded-full pl-5 pr-14 py-4 placeholder:text-black-300 outline-none focus:ring-2 focus:ring-primary"
107+
/>
108+
<button
109+
type="button"
110+
onclick={() => {
111+
showAnswer = !showAnswer;
112+
}}
113+
aria-label={showAnswer ? "Hide answer" : "Show answer"}
114+
aria-pressed={showAnswer}
115+
class="absolute inset-y-0 right-0 flex items-center pr-5 text-black-500 active:opacity-70"
116+
>
117+
<HugeiconsIcon
118+
icon={showAnswer ? ViewOffIcon : ViewIcon}
119+
size={20}
120+
color="currentColor"
121+
strokeWidth={2}
122+
/>
123+
</button>
124+
</div>
99125
{#if answer.length > PERSONAL_BINDING_MAX_LENGTH - 150}
100126
<p class="text-xs text-black-500 text-right mt-1">
101127
{answer.length} / {PERSONAL_BINDING_MAX_LENGTH}

infrastructure/eid-wallet/src/routes/(public)/recover/+page.svelte

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import BottomSheet from "$lib/ui/BottomSheet/BottomSheet.svelte";
1313
import LoadingSheet from "$lib/ui/LoadingSheet/LoadingSheet.svelte";
1414
import { capitalize } from "$lib/utils";
1515
import { PERSONAL_BINDING_BY_TYPE_QUERY } from "$lib/utils/personalBinding";
16-
import { ArrowLeft01Icon } from "@hugeicons/core-free-icons";
16+
import {
17+
ArrowLeft01Icon,
18+
ViewIcon,
19+
ViewOffIcon,
20+
} from "@hugeicons/core-free-icons";
1721
import { HugeiconsIcon } from "@hugeicons/svelte";
1822
import {
1923
Format,
@@ -124,6 +128,9 @@ let enameLoading = $state(false);
124128
let answerInput = $state("");
125129
let answerError = $state<string | null>(null);
126130
let answerLoading = $state(false);
131+
// The security answer is sensitive — mask it by default, with an eye toggle
132+
// to reveal (spelling matters, so the user needs to be able to check it).
133+
let showAnswer = $state(false);
127134
128135
// Question text + envelope id are pulled from the target eVault during
129136
// handleSubmitEname. The envelope id is the metaEnvelopeId of the
@@ -554,6 +561,7 @@ async function handleSubmitEname() {
554561
answerInput = "";
555562
answerError = null;
556563
lockedUntilLabel = null;
564+
showAnswer = false;
557565
step = "unverified-answer";
558566
} catch (err: unknown) {
559567
console.error("[RECOVERY/unverified] eName lookup error:", err);
@@ -1337,17 +1345,35 @@ onMount(() => {
13371345
>
13381346
{securityQuestion} <span class="text-danger">*</span>
13391347
</label>
1340-
<input
1341-
id="recover-answer"
1342-
type="text"
1343-
bind:value={answerInput}
1344-
autocomplete="off"
1345-
autocapitalize="off"
1346-
autocorrect="off"
1347-
spellcheck="false"
1348-
placeholder="Your answer"
1349-
class="w-full bg-card-alternative rounded-full px-5 py-4 placeholder:text-black-300 outline-none focus:ring-2 focus:ring-primary"
1350-
/>
1348+
<div class="relative">
1349+
<input
1350+
id="recover-answer"
1351+
type={showAnswer ? "text" : "password"}
1352+
bind:value={answerInput}
1353+
autocomplete="off"
1354+
autocapitalize="off"
1355+
autocorrect="off"
1356+
spellcheck="false"
1357+
placeholder="Your answer"
1358+
class="w-full bg-card-alternative rounded-full pl-5 pr-14 py-4 placeholder:text-black-300 outline-none focus:ring-2 focus:ring-primary"
1359+
/>
1360+
<button
1361+
type="button"
1362+
onclick={() => {
1363+
showAnswer = !showAnswer;
1364+
}}
1365+
aria-label={showAnswer ? "Hide answer" : "Show answer"}
1366+
aria-pressed={showAnswer}
1367+
class="absolute inset-y-0 right-0 flex items-center pr-5 text-black-500 active:opacity-70"
1368+
>
1369+
<HugeiconsIcon
1370+
icon={showAnswer ? ViewOffIcon : ViewIcon}
1371+
size={20}
1372+
color="currentColor"
1373+
strokeWidth={2}
1374+
/>
1375+
</button>
1376+
</div>
13511377
{#if answerError}
13521378
<p class="text-danger text-sm font-medium" role="alert">
13531379
{answerError}

0 commit comments

Comments
 (0)