Updated 2FA forms

This commit is contained in:
Antoine Nguyen
2024-06-22 19:45:54 +02:00
committed by Spitfireap
parent 1b6f48a5c1
commit 03151f76f2
4 changed files with 163 additions and 138 deletions
@@ -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
}
})
}
+8 -11
View File
@@ -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 {
+49 -50
View File
@@ -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>