filebrowser/frontend/src/components/prompts/Otp.vue
KhashayarKhm a18583640c feat(frontend): add TOTP UI components and dependency
- add OTP modal component with its css file
- add Profile2FA component for 2FA section in settings page
- add @scure/base package to encode OTP secrets in Base32, enabling alternative import options for authenticator apps
- add new phrases to the en.json localization file
2025-04-29 13:46:22 +03:30

86 lines
2.2 KiB
Vue

<template>
<div class="card floating otp-modal">
<div class="card-title">
<h2>{{ t("otp.name") }}</h2>
<p>{{ t("otp.verifyInstructions") }}</p>
</div>
<div class="card-content">
<input
v-model.trim="totpCode"
:class="inputClassObject"
:placeholder="t('otp.codeInputPlaceholder')"
@keyup.enter="submit"
id="focus-prompt"
tabindex="1"
class="input input--block"
type="text"
pattern="[0-9]*"
inputmode="numeric"
maxlength="6"
required
autocomplete="one-time-code"
aria-describedby="totp-error"
/>
</div>
<div class="card-action">
<button
class="button button--flat button--grey"
@click="layoutStore.closeHovers"
:aria-label="t('buttons.cancel')"
:title="t('buttons.cancel')"
tabindex="3"
>
{{ t("buttons.cancel") }}
</button>
<button
class="button button--flat"
:aria-label="t('buttons.verify')"
:title="t('buttons.verify')"
@click="submit"
tabindex="2"
>
{{ t("buttons.verify") }}
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { inject, ref } from "vue";
import { useLayoutStore } from "@/stores/layout";
import { useI18n } from "vue-i18n";
import { StatusError } from "@/api/utils";
import { computed } from "vue";
const $showError = inject<IToastError>("$showError")!;
const layoutStore = useLayoutStore();
const { t } = useI18n();
const totpCode = ref<string>("");
const inputClassObject = computed(() => ({
empty: totpCode.value === "",
}));
const submit = async (event: Event) => {
event.preventDefault();
event.stopPropagation();
if (totpCode.value.length !== 6 || !/^\d+$/.test(totpCode.value)) {
throw new Error(t("otp.invalidCodeType"));
}
try {
await layoutStore.currentPrompt?.confirm(totpCode.value);
} catch (e) {
if (e instanceof StatusError) {
console.error("TOTP Verification Error:", e);
$showError(t("otp.verificationFailed"));
} else if (e instanceof Error) {
$showError(e);
}
}
layoutStore.closeHovers();
};
</script>