feat(auth): harden authentication and add configurable two-factor support
This commit is contained in:
78
resources/js/components/auth/RecoveryCodesPanel.vue
Normal file
78
resources/js/components/auth/RecoveryCodesPanel.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
codes: string[]
|
||||
}>()
|
||||
|
||||
const copied = ref(false)
|
||||
|
||||
function recoveryCodesText() {
|
||||
return [
|
||||
'Two-factor authentication recovery codes',
|
||||
'',
|
||||
...props.codes,
|
||||
'',
|
||||
'Each code can only be used once. Store these codes securely.',
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
async function copyAll() {
|
||||
await navigator.clipboard.writeText(props.codes.join('\n'))
|
||||
copied.value = true
|
||||
|
||||
window.setTimeout(() => {
|
||||
copied.value = false
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
function download() {
|
||||
const blob = new Blob([recoveryCodesText()], {
|
||||
type: 'text/plain;charset=utf-8',
|
||||
})
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
|
||||
link.href = url
|
||||
link.download = 'two-factor-recovery-codes.txt'
|
||||
link.click()
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<UButton
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
:color="copied ? 'success' : 'neutral'"
|
||||
:icon="copied ? 'i-lucide-check' : 'i-lucide-copy'"
|
||||
:aria-label="copied ? 'Recovery codes copied' : 'Copy all recovery codes'"
|
||||
@click="copyAll"
|
||||
>
|
||||
{{ copied ? 'Copied' : 'Copy all' }}
|
||||
</UButton>
|
||||
<UButton
|
||||
type="button"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
icon="i-lucide-download"
|
||||
aria-label="Download recovery codes"
|
||||
@click="download"
|
||||
>
|
||||
Download
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2 sm:grid-cols-2">
|
||||
<code
|
||||
v-for="code in codes"
|
||||
:key="code"
|
||||
class="rounded bg-elevated px-3 py-2 text-center text-sm"
|
||||
>{{ code }}</code>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user