Added customization to webauthn keys

This commit is contained in:
Spitap
2024-06-22 19:45:24 +02:00
committed by Spitfireap
parent 68ac5cba33
commit 01231c4a59
7 changed files with 133 additions and 12 deletions
+3
View File
@@ -22,4 +22,7 @@ export default {
getAllFidoRegistred() {
return repository.get('/fido/')
},
deleteFido(id) {
return repository.delete(`/fido/${id}/`)
}
}
@@ -7,13 +7,51 @@
</span>
</v-card-title>
<v-card-text>
<div tag="p" class="my-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 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) }}
</template>
<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 v-slot: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
@@ -34,13 +72,24 @@
</v-card-text>
</v-card>
</div>
<ConfirmDialog ref="confirmDeletion"/>
<ConfirmDialog ref="confirmEdition">
<v-form ref="editForm">
<v-text-field v-model="editName" :label="$gettext('name')"/>
<v-switch v-model="editEnabled" :label="$gettext('enabled')" color="principal"/>
</v-form>
</ConfirmDialog>
</template>
<script setup lang="js">
import { ref, onMounted } from 'vue'
import { ref, onMounted, computed } from 'vue'
import authApi from '@/api/auth.js'
import { useGettext } from 'vue3-gettext'
import rules from '@/plugins/rules.js'
import { useAuthStore } from '@/stores'
import ConfirmDialog from '@/components/tools/ConfirmDialog.vue'
const authStore = useAuthStore()
const { $gettext } = useGettext()
@@ -49,6 +98,21 @@ const registrationLoading = ref(false)
const creationOption = ref()
const browserCapable = !!(navigator.credentials && navigator.credentials.create && navigator.credentials.get && window.PublicKeyCredential)
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' },
{ title: $gettext('Use count'), key: 'use_count' },
{ title: $gettext('Enabled'), key: 'enabled' },
{ title: $gettext('Actions'), key: 'actions'}
]
const confirmDeletion = ref()
const confirmEdition = ref()
const editForm = ref()
const editName = ref('')
const editEnabled = ref(false)
async function startFidoRegistration() {
const { valid } = await fidoForm.value.validate()
@@ -60,19 +124,48 @@ async function startFidoRegistration() {
.then(function (attestation) {
const result = createResponseToJSON(attestation)
result.name = name.value
console.log(result)
authApi.endFidoRegistration(result).then((resp) => {
console.log(resp)
})
authStore.addFidoCred(result)
})
}
}
async function deleteCred(cred) {
const result = await confirmDeletion.value.open(
$gettext('Warning'),
$gettext('Do you really want to delete this WebaAuthn device?'),
{
color: 'warning',
cancelLabel: $gettext('No'),
agreeLabel: $gettext('Yes'),
}
)
if (!result) {
return
}
authStore.deleteFidoCreds(cred.id)
}
async function editCred(cred) {
editName.value = cred.name
editEnabled.value = cred.enabled
const result = await confirmEdition.value.open(
$gettext('Edition'),
$gettext('Edit your device'),
{
color: 'warning',
cancelLabel: $gettext('Cancel'),
agreeLabel: $gettext('Save'),
}
)
if (!result) {
return
}
console.log(editForm.value.items)
}
onMounted(() => {
registrationLoading.value = true
authApi.getAllFidoRegistred().then((resp) => {
console.log(resp)
})
authStore.getFidoCreds()
authApi.beginFidoRegistration().then((resp) => {
creationOption.value = createRequestFromJSON(resp.data)
}).finally(() => registrationLoading.value = false)
+1 -1
View File
@@ -10,7 +10,7 @@ export default {
}
app.config.globalProperties.$date = (value) => {
return DateTime.fromISO(value)
.setLocale('en')
.setLocale(gettext.current)
.toLocaleString(DateTime.DATETIME_MED)
}
app.config.globalProperties.$truncate = (value, length, clamp) => {
+24
View File
@@ -9,6 +9,7 @@ import { UserManager } from 'oidc-client-ts'
import repository from '@/api/repository'
import accountApi from '@/api/account'
import accountsApi from '@/api/accounts'
import authApi from '@/api/auth.js'
import { useGlobalConfig } from '@/main'
@@ -28,6 +29,7 @@ export const useAuthStore = defineStore('auth', () => {
filterProtocolClaims: true,
loadUserInfo: true,
})
const fidoCreds = ref([])
const userHasMailbox = computed(() => {
return authUser.value.mailbox !== null
@@ -41,6 +43,24 @@ export const useAuthStore = defineStore('auth', () => {
})
}
async function getFidoCreds() {
return authApi.getAllFidoRegistred().then((resp) => {
fidoCreds.value = resp.data
})
}
async function addFidoCred(result) {
return authApi.endFidoRegistration(result).then(() => {
getFidoCreds()
})
}
async function deleteFidoCreds(id) {
return authApi.deleteFido(id).then(() => {
fidoCreds.value = fidoCreds.value.filter((cred) => cred.id !== id)
})
}
function getAccessToken() {
return manager.getUser().then((user) => {
if (!user) {
@@ -138,9 +158,13 @@ export const useAuthStore = defineStore('auth', () => {
completeLogin,
isAuthenticated,
userHasMailbox,
fidoCreds,
validateAccess,
fetchUser,
getAccessToken,
getFidoCreds,
addFidoCred,
deleteFidoCreds,
initialize,
login,
$reset,
+1
View File
@@ -249,6 +249,7 @@ class FIDOSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserFidoKeys
fields = ["id", "name", "enabled", "added_on", "last_used", "use_count"]
# TODO : read-only fields for everything other than name and enabled :)
class FidoRegistrationSerializer(serializers.Serializer):
+1 -1
View File
@@ -27,7 +27,7 @@ def delete_cache_key(class_target, throttles, request):
"""Attempt to delete cache key from throttling on login/password reset success."""
for throttle in throttles:
if type(throttle) is class_target:
if type(throttle) == class_target:
throttle.reset_cache(request)
return
+1 -1
View File
@@ -206,7 +206,7 @@ class FIDOViewSet(GetThrottleViewsetMixin,
if not request.user.tfa_enabled:
request.user.tfa_enabled = True
request.user.save()
return response.Response({"success": True}, 200)
return response.Response(status=204)
@action(methods=["post"], detail=False, url_path="authenticate/begin")
def authenticate_begin(self, request):