mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
Updated 2FA forms
This commit is contained in:
@@ -2,68 +2,90 @@
|
||||
<div>
|
||||
<v-card flat>
|
||||
<v-card-title>
|
||||
<span class="text-subtitle-1">
|
||||
<span class="text-h6">
|
||||
{{ $gettext('WebAuthn') }}
|
||||
</span>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<template v-if="fidoCreds.length === 0">
|
||||
<div tag="p" class="my-4">
|
||||
{{
|
||||
$gettext(
|
||||
"You don't have any WebAuthN registered as a second authentication method."
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<h3>{{ $gettext('Registered Webauthn devices') }}</h3>
|
||||
<v-data-table-virtual
|
||||
:headers="headers"
|
||||
:items="fidoCreds"
|
||||
height="400"
|
||||
item-value="id"
|
||||
>
|
||||
<template #[`item.added_on`]="{ item }">
|
||||
{{ $date(item.added_on) }}
|
||||
<v-row>
|
||||
<v-col v-if="!authStore.authUser.tfa_enabled" cols="4">
|
||||
<div tag="p" class="my-4">
|
||||
{{
|
||||
$gettext(
|
||||
'You must enable two-factor authentication using one-time passwords first.'
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col v-else cols="4">
|
||||
<template v-if="fidoCreds.length === 0">
|
||||
<div tag="p" class="my-4">
|
||||
{{
|
||||
$gettext(
|
||||
"You don't have any WebAuthN registered as a second authentication method."
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
<template #[`item.last_used`]="{ item }">
|
||||
<template v-if="item.last_used === null">
|
||||
{{ $gettext('Never') }}
|
||||
<template v-if="browserCapable">
|
||||
<v-form ref="fidoForm" @submit.prevent="startFidoRegistration">
|
||||
<v-text-field
|
||||
v-model="name"
|
||||
:label="$gettext('New device name')"
|
||||
:rules="[rules.required]"
|
||||
/>
|
||||
<v-btn
|
||||
color="success"
|
||||
type="submit"
|
||||
:loading="registrationLoading"
|
||||
>
|
||||
{{ $gettext('Setup device') }}
|
||||
</v-btn>
|
||||
</v-form>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-alert type="error">
|
||||
{{
|
||||
$gettext(
|
||||
'Your browser does not seem compatible with WebAuthN'
|
||||
)
|
||||
}}
|
||||
</v-alert>
|
||||
</template>
|
||||
</v-col>
|
||||
<v-col v-if="fidoCreds.length" cols="8">
|
||||
<h3>{{ $gettext('Registered Webauthn devices') }}</h3>
|
||||
<v-data-table-virtual
|
||||
:headers="headers"
|
||||
:items="fidoCreds"
|
||||
height="400"
|
||||
item-value="id"
|
||||
>
|
||||
<template #[`item.added_on`]="{ item }">
|
||||
{{ $date(item.added_on) }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $date(item.last_used) }}
|
||||
<template #[`item.last_used`]="{ item }">
|
||||
<template v-if="item.last_used === null">
|
||||
{{ $gettext('Never') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $date(item.last_used) }}
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
<template #[`item.actions`]="{ item }">
|
||||
<v-icon class="me-2" size="small" @click="editCred(item)">
|
||||
mdi-pencil
|
||||
</v-icon>
|
||||
<v-icon size="small" @click="deleteCred(item)">
|
||||
mdi-delete
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-data-table-virtual>
|
||||
</template>
|
||||
<template v-if="browserCapable">
|
||||
<v-form ref="fidoForm" @submit.prevent="startFidoRegistration">
|
||||
<v-text-field
|
||||
v-model="name"
|
||||
:label="$gettext('Name')"
|
||||
:rules="[rules.required]"
|
||||
/>
|
||||
<v-btn color="success" type="submit" :loading="registrationLoading">
|
||||
{{ $gettext('Add WebAuthN device') }}
|
||||
</v-btn>
|
||||
</v-form>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-alert type="error">
|
||||
{{
|
||||
$gettext('Your browser does not seem compatible with WebAuthN')
|
||||
}}
|
||||
</v-alert>
|
||||
</template>
|
||||
<template #[`item.enabled`]="{ item }">
|
||||
{{ $yesno(item.enabled) }}
|
||||
</template>
|
||||
<template #[`item.actions`]="{ item }">
|
||||
<v-icon class="me-2" size="small" @click="editCred(item)">
|
||||
mdi-pencil
|
||||
</v-icon>
|
||||
<v-icon size="small" @click="deleteCred(item)">
|
||||
mdi-delete
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-data-table-virtual>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</div>
|
||||
@@ -111,7 +133,6 @@ const browserCapable = !!(
|
||||
const fidoForm = ref()
|
||||
const fidoCreds = computed(() => authStore.fidoCreds)
|
||||
const headers = [
|
||||
{ title: '#', key: 'id' },
|
||||
{ title: $gettext('Name'), key: 'name' },
|
||||
{ title: $gettext('Added On'), key: 'added_on' },
|
||||
{ title: $gettext('Last Used'), key: 'last_used' },
|
||||
@@ -133,21 +154,22 @@ async function startFidoRegistration() {
|
||||
return
|
||||
}
|
||||
const creationOption = await authApi.beginFidoRegistration()
|
||||
console.log(creationOption)
|
||||
if (creationOption) {
|
||||
const options = parseCreationOptionsFromJSON(creationOption.data)
|
||||
const result = await create(options)
|
||||
const response = result.toJSON()
|
||||
response.name = name.value
|
||||
console.log(response)
|
||||
authStore.addFidoCred(response)
|
||||
response.name = name.value
|
||||
await authStore.addFidoCred(response)
|
||||
fidoForm.value.reset()
|
||||
}
|
||||
registrationLoading.value = false
|
||||
}
|
||||
|
||||
async function deleteCred(cred) {
|
||||
const result = await confirmDeletion.value.open(
|
||||
$gettext('Warning'),
|
||||
$gettext('Do you really want to delete this WebaAuthn device?'),
|
||||
$gettext('Do you really want to delete this WebAuthn device?'),
|
||||
{
|
||||
color: 'warning',
|
||||
cancelLabel: $gettext('No'),
|
||||
@@ -169,7 +191,7 @@ async function editCred(cred) {
|
||||
editEnabled.value = cred.enabled
|
||||
|
||||
const result = await confirmEdition.value.open(
|
||||
$gettext('Edtion'),
|
||||
$gettext('Edition'),
|
||||
$gettext('Edit your device'),
|
||||
{
|
||||
color: 'warning',
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<div>
|
||||
<v-card flat>
|
||||
<v-card-title>
|
||||
<span class="text-subtitle-1">
|
||||
{{ $gettext('Two factor authentication') }}
|
||||
<span class="text-h6">
|
||||
{{ $gettext('One-time passwords') }}
|
||||
</span>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
@@ -37,7 +37,10 @@
|
||||
}}
|
||||
</v-alert>
|
||||
<label class="m-label">{{ $gettext('Pin code') }}</label>
|
||||
<v-otp-input v-model="pinCode" />
|
||||
<v-otp-input
|
||||
v-model="pinCode"
|
||||
:error="pinCodeErrors.length !== 0"
|
||||
/>
|
||||
<v-btn color="primary" @click="finalizeTFASetup">
|
||||
{{ $gettext('Register') }}
|
||||
</v-btn>
|
||||
@@ -48,27 +51,31 @@
|
||||
<v-alert type="success">
|
||||
{{
|
||||
$gettext(
|
||||
'Congratulations! Two-Factor Authentication is now enabled for your account.'
|
||||
'Congratulations! Two-factor authentication using one-time passwords is now enabled for your account.'
|
||||
)
|
||||
}}
|
||||
</v-alert>
|
||||
<p>
|
||||
<p class="mt-4">
|
||||
{{
|
||||
$gettext(
|
||||
"The following recovery codes can be used one time each to let you regain access to your account, in case you lose your phone for example. Make sure to save them in a safe place, otherwise you won't be able to access your account anymore."
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<ul>
|
||||
<li v-for="token in tokens" :key="token">
|
||||
{{ token }}
|
||||
</li>
|
||||
</ul>
|
||||
<v-table density="compact">
|
||||
<tbody>
|
||||
<tr v-for="token in tokens" :key="token">
|
||||
<td>{{ token }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-table>
|
||||
</template>
|
||||
<template v-else-if="tfa_enabled">
|
||||
<v-alert type="info" class="mb-2">
|
||||
{{
|
||||
$gettext('Two-Factor Authentication is enabled for your account.')
|
||||
$gettext(
|
||||
'Two-factor authentication using one-time passwords is enabled for your account.'
|
||||
)
|
||||
}}
|
||||
</v-alert>
|
||||
<v-form ref="editTFAForm">
|
||||
@@ -100,12 +107,12 @@
|
||||
<div tag="p" class="my-4">
|
||||
{{
|
||||
$gettext(
|
||||
"Two-Factor Authentication (2FA) is not yet activated for your account. Enabling this feature will increase your account's security."
|
||||
'Two-factor authentication (2FA) using one-time passwords is not yet activated for your account.'
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
<v-btn color="success" @click="startTFASetup">
|
||||
{{ $gettext('Enable 2FA') }}
|
||||
{{ $gettext('Register authenticator') }}
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-card-text>
|
||||
@@ -136,7 +143,7 @@ const editTFAForm = ref()
|
||||
|
||||
const newTokens = ref([])
|
||||
const pinCode = ref('')
|
||||
const pinCodeErrors = ref()
|
||||
const pinCodeErrors = ref([])
|
||||
const key = ref(null)
|
||||
const qrURL = ref(null)
|
||||
const clicked = ref(false)
|
||||
@@ -167,7 +174,7 @@ function startTFASetup() {
|
||||
})
|
||||
}
|
||||
|
||||
async function finalizeTFASetup() {
|
||||
function finalizeTFASetup() {
|
||||
authStore
|
||||
.finalizeTFASetup(pinCode.value)
|
||||
.then((response) => {
|
||||
@@ -177,7 +184,7 @@ async function finalizeTFASetup() {
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.response.status === 400) {
|
||||
pinCodeErrors.value = error.response.data
|
||||
pinCodeErrors.value = error.response.data.pin_code
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -140,17 +140,14 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
})
|
||||
}
|
||||
|
||||
async function finalizeTFASetup(pinCode) {
|
||||
return accountApi
|
||||
.finalizeTFASetup(pinCode)
|
||||
.then((response) => {
|
||||
const cookie = Cookies.withAttributes({ sameSite: 'strict' })
|
||||
cookie.set('token', response.data.access)
|
||||
cookie.set('refreshToken', response.data.refresh)
|
||||
initialize()
|
||||
return response
|
||||
})
|
||||
.catch((error) => error)
|
||||
function finalizeTFASetup(pinCode) {
|
||||
return accountApi.finalizeTFASetup(pinCode).then((response) => {
|
||||
const cookie = Cookies.withAttributes({ sameSite: 'strict' })
|
||||
cookie.set('token', response.data.access)
|
||||
cookie.set('refreshToken', response.data.refresh)
|
||||
fetchUser()
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,48 +1,47 @@
|
||||
<template>
|
||||
<v-toolbar flat>
|
||||
<v-toolbar-title>{{ $gettext('Account settings') }}</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<div class="pa-4">
|
||||
<v-tabs
|
||||
v-model="tab"
|
||||
color="primary"
|
||||
class="mb-4"
|
||||
>
|
||||
<v-tab value="profile">
|
||||
{{ $gettext('Profile') }}
|
||||
</v-tab>
|
||||
<v-tab value="security">
|
||||
{{ $gettext('Security') }}
|
||||
</v-tab>
|
||||
<v-tab value="forward" v-if="authStore.userHasMailbox">
|
||||
{{ $gettext('Forward') }}
|
||||
</v-tab>
|
||||
<v-tab value="autoreply" v-if="authStore.userHasMailbox">
|
||||
{{ $gettext('Auto-reply message') }}
|
||||
</v-tab>
|
||||
<v-tab value="api" v-if="authStore.authUser.role === 'SuperAdmins'">
|
||||
{{ $gettext('API access') }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<v-window v-model="tab">
|
||||
<v-window-item value="profile">
|
||||
<ProfileForm />
|
||||
</v-window-item>
|
||||
<v-window-item value="security">
|
||||
<TwoFactorAuthForm />
|
||||
<FidoAuthForm />
|
||||
</v-window-item>
|
||||
<v-window-item value="forward" v-if="authStore.userHasMailbox">
|
||||
<ForwardForm />
|
||||
</v-window-item>
|
||||
<v-window-item value="autoreply" v-if="authStore.userHasMailbox">
|
||||
<AutoReplyForm />
|
||||
</v-window-item>
|
||||
<v-window-item value="api" v-if="authStore.authUser.role === 'SuperAdmins'">
|
||||
<APISetupForm />
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</div>
|
||||
<v-toolbar flat>
|
||||
<v-toolbar-title>{{ $gettext('Account settings') }}</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<div class="pa-4">
|
||||
<v-tabs v-model="tab" color="primary" class="mb-4">
|
||||
<v-tab value="profile">
|
||||
{{ $gettext('Profile') }}
|
||||
</v-tab>
|
||||
<v-tab value="security">
|
||||
{{ $gettext('Two-factor auth') }}
|
||||
</v-tab>
|
||||
<v-tab v-if="authStore.userHasMailbox" value="forward">
|
||||
{{ $gettext('Forward') }}
|
||||
</v-tab>
|
||||
<v-tab v-if="authStore.userHasMailbox" value="autoreply">
|
||||
{{ $gettext('Auto-reply message') }}
|
||||
</v-tab>
|
||||
<v-tab v-if="authStore.authUser.role === 'SuperAdmins'" value="api">
|
||||
{{ $gettext('API access') }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<v-window v-model="tab">
|
||||
<v-window-item value="profile">
|
||||
<ProfileForm />
|
||||
</v-window-item>
|
||||
<v-window-item value="security">
|
||||
<TwoFactorAuthForm />
|
||||
<FidoAuthForm />
|
||||
</v-window-item>
|
||||
<v-window-item v-if="authStore.userHasMailbox" value="forward">
|
||||
<ForwardForm />
|
||||
</v-window-item>
|
||||
<v-window-item v-if="authStore.userHasMailbox" value="autoreply">
|
||||
<AutoReplyForm />
|
||||
</v-window-item>
|
||||
<v-window-item
|
||||
v-if="authStore.authUser.role === 'SuperAdmins'"
|
||||
value="api"
|
||||
>
|
||||
<APISetupForm />
|
||||
</v-window-item>
|
||||
</v-window>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -60,10 +59,10 @@ const tab = ref()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.v-toolbar {
|
||||
background-color: #f7f8fa !important;
|
||||
}
|
||||
.v-tabs {
|
||||
background-color: #f7f8fa !important;
|
||||
}
|
||||
.v-toolbar {
|
||||
background-color: #f7f8fa !important;
|
||||
}
|
||||
.v-tabs {
|
||||
background-color: #f7f8fa !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user