summaryrefslogtreecommitdiffstats
path: root/src/components/TwoFADialog.vue
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/TwoFADialog.vue')
-rw-r--r--src/components/TwoFADialog.vue225
1 files changed, 225 insertions, 0 deletions
diff --git a/src/components/TwoFADialog.vue b/src/components/TwoFADialog.vue
new file mode 100644
index 0000000..6a0bf40
--- /dev/null
+++ b/src/components/TwoFADialog.vue
@@ -0,0 +1,225 @@
+<template>
+ <form @submit.prevent="submit">
+ <div ref="modal" class="modal fade" tabindex="-1" data-bs-backdrop="static">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h5 class="modal-title">
+ {{ $t("Setup 2FA") }}
+ <span v-if="twoFAStatus == true" class="badge bg-primary">{{ $t("Active") }}</span>
+ <span v-if="twoFAStatus == false" class="badge bg-primary">{{ $t("Inactive") }}</span>
+ </h5>
+ <button :disabled="processing" type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" />
+ </div>
+ <div class="modal-body">
+ <div class="mb-3">
+ <div v-if="uri && twoFAStatus == false" class="mx-auto text-center" style="width: 210px;">
+ <vue-qrcode :key="uri" :value="uri" type="image/png" :quality="1" :color="{ light: '#ffffffff' }" />
+ <button v-show="!showURI" type="button" class="btn btn-outline-primary btn-sm mt-2" @click="showURI = true">{{ $t("Show URI") }}</button>
+ </div>
+ <p v-if="showURI && twoFAStatus == false" class="text-break mt-2">{{ uri }}</p>
+
+ <div v-if="!(uri && twoFAStatus == false)" class="mb-3">
+ <label for="current-password" class="form-label">
+ {{ $t("Current Password") }}
+ </label>
+ <input
+ id="current-password"
+ v-model="currentPassword"
+ type="password"
+ class="form-control"
+ autocomplete="current-password"
+ required
+ />
+ </div>
+
+ <button v-if="uri == null && twoFAStatus == false" class="btn btn-primary" type="button" @click="prepare2FA()">
+ {{ $t("Enable 2FA") }}
+ </button>
+
+ <button v-if="twoFAStatus == true" class="btn btn-danger" type="button" :disabled="processing" @click="confirmDisableTwoFA()">
+ {{ $t("Disable 2FA") }}
+ </button>
+
+ <div v-if="uri && twoFAStatus == false" class="mt-3">
+ <label for="basic-url" class="form-label">{{ $t("twoFAVerifyLabel") }}</label>
+ <div class="input-group">
+ <input v-model="token" type="text" maxlength="6" class="form-control" autocomplete="one-time-code" required>
+ <button class="btn btn-outline-primary" type="button" @click="verifyToken()">{{ $t("Verify Token") }}</button>
+ </div>
+ <p v-show="tokenValid" class="mt-2" style="color: green;">{{ $t("tokenValidSettingsMsg") }}</p>
+ </div>
+ </div>
+ </div>
+
+ <div v-if="uri && twoFAStatus == false" class="modal-footer">
+ <button type="submit" class="btn btn-primary" :disabled="processing || tokenValid == false" @click="confirmEnableTwoFA()">
+ <div v-if="processing" class="spinner-border spinner-border-sm me-1"></div>
+ {{ $t("Save") }}
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </form>
+
+ <Confirm ref="confirmEnableTwoFA" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="save2FA">
+ {{ $t("confirmEnableTwoFAMsg") }}
+ </Confirm>
+
+ <Confirm ref="confirmDisableTwoFA" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="disable2FA">
+ {{ $t("confirmDisableTwoFAMsg") }}
+ </Confirm>
+</template>
+
+<script lang="ts">
+import { Modal } from "bootstrap";
+import Confirm from "./Confirm.vue";
+import VueQrcode from "vue-qrcode";
+
+export default {
+ components: {
+ Confirm,
+ VueQrcode,
+ },
+ props: {},
+ data() {
+ return {
+ currentPassword: "",
+ processing: false,
+ uri: null,
+ tokenValid: false,
+ twoFAStatus: null,
+ token: null,
+ showURI: false,
+ };
+ },
+ mounted() {
+ this.modal = new Modal(this.$refs.modal);
+ this.getStatus();
+ },
+ methods: {
+ /**
+ * Show the dialog
+ * @returns {void}
+ */
+ show() {
+ this.modal.show();
+ },
+
+ /**
+ * Show dialog to confirm enabling 2FA
+ * @returns {void}
+ */
+ confirmEnableTwoFA() {
+ this.$refs.confirmEnableTwoFA.show();
+ },
+
+ /**
+ * Show dialog to confirm disabling 2FA
+ * @returns {void}
+ */
+ confirmDisableTwoFA() {
+ this.$refs.confirmDisableTwoFA.show();
+ },
+
+ /**
+ * Prepare 2FA configuration
+ * @returns {void}
+ */
+ prepare2FA() {
+ this.processing = true;
+
+ this.$root.getSocket().emit("prepare2FA", this.currentPassword, (res) => {
+ this.processing = false;
+
+ if (res.ok) {
+ this.uri = res.uri;
+ } else {
+ this.$root.toastError(res.msg);
+ }
+ });
+ },
+
+ /**
+ * Save the current 2FA configuration
+ * @returns {void}
+ */
+ save2FA() {
+ this.processing = true;
+
+ this.$root.getSocket().emit("save2FA", this.currentPassword, (res) => {
+ this.processing = false;
+
+ if (res.ok) {
+ this.$root.toastRes(res);
+ this.getStatus();
+ this.currentPassword = "";
+ this.modal.hide();
+ } else {
+ this.$root.toastError(res.msg);
+ }
+ });
+ },
+
+ /**
+ * Disable 2FA for this user
+ * @returns {void}
+ */
+ disable2FA() {
+ this.processing = true;
+
+ this.$root.getSocket().emit("disable2FA", this.currentPassword, (res) => {
+ this.processing = false;
+
+ if (res.ok) {
+ this.$root.toastRes(res);
+ this.getStatus();
+ this.currentPassword = "";
+ this.modal.hide();
+ } else {
+ this.$root.toastError(res.msg);
+ }
+ });
+ },
+
+ /**
+ * Verify the token generated by the user
+ * @returns {void}
+ */
+ verifyToken() {
+ this.$root.getSocket().emit("verifyToken", this.token, this.currentPassword, (res) => {
+ if (res.ok) {
+ this.tokenValid = res.valid;
+ } else {
+ this.$root.toastError(res.msg);
+ }
+ });
+ },
+
+ /**
+ * Get current status of 2FA
+ * @returns {void}
+ */
+ getStatus() {
+ this.$root.getSocket().emit("twoFAStatus", (res) => {
+ if (res.ok) {
+ this.twoFAStatus = res.status;
+ } else {
+ this.$root.toastError(res.msg);
+ }
+ });
+ },
+ },
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../assets/vars.scss";
+
+.dark {
+ .modal-dialog .form-text, .modal-dialog p {
+ color: $dark-font-color;
+ }
+}
+</style>