web/satellite/vuetify: add recovery MFA codes dialog
Added regenerate MFA codes dialog and functionality. Issue: https://github.com/storj/storj/issues/6091 Change-Id: I5139e83077adb6deea1ad57c0ebe4e84a88e770c
This commit is contained in:
parent
7be844351d
commit
b21041d6f9
@ -32,7 +32,7 @@
|
|||||||
</v-card-item>
|
</v-card-item>
|
||||||
<v-divider class="mx-8" />
|
<v-divider class="mx-8" />
|
||||||
<v-card-item class="px-8 pt-4 pb-0">
|
<v-card-item class="px-8 pt-4 pb-0">
|
||||||
<v-form v-model="formValid" class="pt-1" :onsubmit="disable">
|
<v-form v-model="formValid" class="pt-2">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="confirmCode"
|
v-model="confirmCode"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
</v-card-item>
|
</v-card-item>
|
||||||
<v-divider class="mx-8" />
|
<v-divider class="mx-8" />
|
||||||
<v-card-item class="px-8 pt-4 pb-0">
|
<v-card-item class="px-8 pt-4 pb-0">
|
||||||
<v-form v-model="formValid" class="pt-1" :onsubmit="enable">
|
<v-form v-model="formValid" class="pt-2">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="confirmPasscode"
|
v-model="confirmPasscode"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
@ -0,0 +1,98 @@
|
|||||||
|
// Copyright (C) 2023 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<v-dialog
|
||||||
|
v-model="model"
|
||||||
|
width="auto"
|
||||||
|
min-width="320px"
|
||||||
|
max-width="460px"
|
||||||
|
transition="fade-transition"
|
||||||
|
>
|
||||||
|
<v-card rounded="xlg">
|
||||||
|
<v-card-item class="pl-7 pr-0 pb-5 pt-0">
|
||||||
|
<v-row align="start" justify="space-between" class="ma-0">
|
||||||
|
<v-row align="center" class="ma-0 pt-5">
|
||||||
|
<img class="flex-shrink-0" src="@poc/assets/icon-mfa.svg" alt="MFA">
|
||||||
|
<v-card-title class="font-weight-bold ml-4">Two-Factor Recovery Codes</v-card-title>
|
||||||
|
</v-row>
|
||||||
|
<v-btn
|
||||||
|
icon="$close"
|
||||||
|
variant="text"
|
||||||
|
size="small"
|
||||||
|
color="default"
|
||||||
|
@click="model = false"
|
||||||
|
/>
|
||||||
|
</v-row>
|
||||||
|
</v-card-item>
|
||||||
|
<v-divider class="mx-8" />
|
||||||
|
<v-card-item class="px-8 py-4">
|
||||||
|
<p>Please save these codes somewhere to be able to recover access to your account.</p>
|
||||||
|
</v-card-item>
|
||||||
|
<v-divider class="mx-8" />
|
||||||
|
<v-card-item class="px-8 py-4">
|
||||||
|
<p
|
||||||
|
v-for="(code, index) in userMFARecoveryCodes"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
{{ code }}
|
||||||
|
</p>
|
||||||
|
</v-card-item>
|
||||||
|
<v-divider class="mx-8 mb-4" />
|
||||||
|
<v-card-actions dense class="px-7 pb-5 pt-0">
|
||||||
|
<v-col class="px-0">
|
||||||
|
<v-btn
|
||||||
|
color="primary"
|
||||||
|
variant="flat"
|
||||||
|
block
|
||||||
|
@click="model = false"
|
||||||
|
>
|
||||||
|
Done
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import {
|
||||||
|
VBtn,
|
||||||
|
VCard,
|
||||||
|
VCardActions,
|
||||||
|
VCardItem,
|
||||||
|
VCardTitle,
|
||||||
|
VCol,
|
||||||
|
VDialog,
|
||||||
|
VDivider,
|
||||||
|
VRow,
|
||||||
|
} from 'vuetify/components';
|
||||||
|
|
||||||
|
import { AuthHttpApi } from '@/api/auth';
|
||||||
|
import { useUsersStore } from '@/store/modules/usersStore';
|
||||||
|
|
||||||
|
const auth: AuthHttpApi = new AuthHttpApi();
|
||||||
|
|
||||||
|
const usersStore = useUsersStore();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: boolean,
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(event: 'update:modelValue', value: boolean): void,
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const model = computed<boolean>({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: value => emit('update:modelValue', value),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns user MFA recovery codes from store.
|
||||||
|
*/
|
||||||
|
const userMFARecoveryCodes = computed((): string[] => {
|
||||||
|
return usersStore.state.userMFARecoveryCodes;
|
||||||
|
});
|
||||||
|
</script>
|
@ -86,9 +86,12 @@
|
|||||||
</v-list-item-subtitle>
|
</v-list-item-subtitle>
|
||||||
|
|
||||||
<template #append>
|
<template #append>
|
||||||
<v-list-item-action>
|
<v-list-item-action justify="end" class="flex-column flex-sm-row align-end">
|
||||||
<v-btn v-if="!user.isMFAEnabled" size="small" @click="toggleEnableMFADialog">Enable Two-factor</v-btn>
|
<v-btn v-if="!user.isMFAEnabled" size="small" @click="toggleEnableMFADialog">Enable Two-factor</v-btn>
|
||||||
<v-btn v-else variant="outlined" color="default" size="small" @click="isDisableMFADialogShown = true">Disable Two-factor</v-btn>
|
<template v-else>
|
||||||
|
<v-btn class="mb-1 mb-sm-0 mr-sm-1" variant="outlined" color="default" size="small" @click="toggleRecoveryCodesDialog">Regenerate Recovery Codes</v-btn>
|
||||||
|
<v-btn variant="outlined" color="default" size="small" @click="isDisableMFADialogShown = true">Disable Two-factor</v-btn>
|
||||||
|
</template>
|
||||||
</v-list-item-action>
|
</v-list-item-action>
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
@ -156,6 +159,10 @@
|
|||||||
v-model="isDisableMFADialogShown"
|
v-model="isDisableMFADialogShown"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<MFACodesDialog
|
||||||
|
v-model="isRecoveryCodesDialogShown"
|
||||||
|
/>
|
||||||
|
|
||||||
<SetSessionTimeoutDialog
|
<SetSessionTimeoutDialog
|
||||||
v-model="isSetSessionTimeoutDialogShown"
|
v-model="isSetSessionTimeoutDialogShown"
|
||||||
/>
|
/>
|
||||||
@ -187,6 +194,7 @@ import ChangePasswordDialog from '@poc/components/dialogs/ChangePasswordDialog.v
|
|||||||
import ChangeNameDialog from '@poc/components/dialogs/ChangeNameDialog.vue';
|
import ChangeNameDialog from '@poc/components/dialogs/ChangeNameDialog.vue';
|
||||||
import EnableMFADialog from '@poc/components/dialogs/EnableMFADialog.vue';
|
import EnableMFADialog from '@poc/components/dialogs/EnableMFADialog.vue';
|
||||||
import DisableMFADialog from '@poc/components/dialogs/DisableMFADialog.vue';
|
import DisableMFADialog from '@poc/components/dialogs/DisableMFADialog.vue';
|
||||||
|
import MFACodesDialog from '@poc/components/dialogs/MFACodesDialog.vue';
|
||||||
import SetSessionTimeoutDialog from '@poc/components/dialogs/SetSessionTimeoutDialog.vue';
|
import SetSessionTimeoutDialog from '@poc/components/dialogs/SetSessionTimeoutDialog.vue';
|
||||||
|
|
||||||
const usersStore = useUsersStore();
|
const usersStore = useUsersStore();
|
||||||
@ -196,6 +204,7 @@ const isChangePasswordDialogShown = ref<boolean>(false);
|
|||||||
const isChangeNameDialogShown = ref<boolean>(false);
|
const isChangeNameDialogShown = ref<boolean>(false);
|
||||||
const isEnableMFADialogShown = ref<boolean>(false);
|
const isEnableMFADialogShown = ref<boolean>(false);
|
||||||
const isDisableMFADialogShown = ref<boolean>(false);
|
const isDisableMFADialogShown = ref<boolean>(false);
|
||||||
|
const isRecoveryCodesDialogShown = ref<boolean>(false);
|
||||||
const isSetSessionTimeoutDialogShown = ref<boolean>(false);
|
const isSetSessionTimeoutDialogShown = ref<boolean>(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -221,6 +230,15 @@ async function toggleEnableMFADialog() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function toggleRecoveryCodesDialog() {
|
||||||
|
try {
|
||||||
|
await usersStore.generateUserMFARecoveryCodes();
|
||||||
|
isRecoveryCodesDialogShown.value = true;
|
||||||
|
} catch (error) {
|
||||||
|
notify.notifyError(error, AnalyticsErrorEventSource.ACCOUNT_SETTINGS_AREA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
usersStore.getUser(),
|
usersStore.getUser(),
|
||||||
|
Loading…
Reference in New Issue
Block a user