Files
paskia/frontend/src/components/DeviceLinkView.vue
T
LeoVasanko 1bed2c39d8 Implement code word based remote authentication (#1)
Add comprehensive remote authentication system allowing users to log in from one device by authenticating from another trusted device. Features include:

- Proof of Work (PoW) protection using PBKDF2-SHA512 to prevent abuse
- Simple pairing codes (3 words) protected by dynamic PoW difficulty
- Autocomplete pairing code input with error checking
- Real-time WebSocket communication between devices

Unlike device addition links and reset links with QR codes that only allow adding an authentication method, and that work offline over the duration of several days, this mechanism is strictly online, with 5 minute time limit.
2025-12-08 23:56:48 +00:00

68 lines
1.6 KiB
Vue

<template>
<section class="view-root view-root--narrow view-device-link">
<header class="view-header">
<h1>📱 Add Another Device</h1>
<p class="view-lede">Generate a one-time link to set up passkeys on a new device.</p>
</header>
<div class="button-row" style="margin-top:1rem;">
<button @click="showModal = true" class="btn-primary">Generate Registration Link</button>
<button @click="authStore.currentView = 'profile'" class="btn-secondary">Back to Profile</button>
</div>
<RegistrationLinkModal
v-if="showModal"
endpoint="/auth/api/user/create-link"
:user-name="userName"
@close="showModal = false"
@copied="onCopied"
/>
</section>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useAuthStore } from '@/stores/auth'
import RegistrationLinkModal from '@/components/RegistrationLinkModal.vue'
const authStore = useAuthStore()
const userName = ref(null)
const showModal = ref(false)
const onCopied = () => {
authStore.showMessage('Link copied to clipboard!', 'success', 2500)
}
onMounted(async () => {
// Extract optional admin-provided query parameters (?user=Name&emoji=😀)
const params = new URLSearchParams(location.search)
const qUser = params.get('user')
if (qUser) userName.value = qUser.trim()
})
</script>
<style scoped>
.view-lede {
margin: 0;
color: var(--color-text-muted);
}
.qr-link {
text-decoration: none;
color: var(--color-text);
}
.button-row {
justify-content: flex-start;
}
@media (max-width: 720px) {
.button-row {
flex-direction: column;
}
.button-row button {
width: 100%;
}
}
</style>