Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 44s
Add functionality to log all requests from mobile devices, improving debugging capabilities. Extend the existing passkey endpoint checks to include a new endpoint for cross-device registration. This update aims to provide clearer insights into mobile user interactions with the application.
143 lines
4.3 KiB
Vue
143 lines
4.3 KiB
Vue
<template>
|
|
<div class="min-h-screen bg-gray-900 text-white flex items-center justify-center p-4">
|
|
<div class="max-w-md w-full bg-gray-800 rounded-lg p-6 space-y-4">
|
|
<h1 class="text-2xl font-bold text-center">Passkey-Registrierung</h1>
|
|
|
|
<div v-if="status === 'loading'" class="text-center">
|
|
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-white mx-auto mb-4"></div>
|
|
<p>Lade Registrierungsoptionen...</p>
|
|
</div>
|
|
|
|
<div v-else-if="status === 'waiting'" class="text-center">
|
|
<div class="animate-pulse text-4xl mb-4">🔐</div>
|
|
<p class="text-lg mb-2">Warte auf Passkey-Authentifizierung...</p>
|
|
<p class="text-sm text-gray-400">Bitte bestätigen Sie die Registrierung auf diesem Gerät.</p>
|
|
</div>
|
|
|
|
<div v-else-if="status === 'error'" class="text-center text-red-400">
|
|
<p class="text-lg font-semibold mb-2">Fehler</p>
|
|
<p>{{ errorMessage }}</p>
|
|
<button
|
|
@click="retry"
|
|
class="mt-4 px-4 py-2 bg-primary-600 hover:bg-primary-700 rounded"
|
|
>
|
|
Erneut versuchen
|
|
</button>
|
|
</div>
|
|
|
|
<div v-else-if="status === 'success'" class="text-center text-green-400">
|
|
<p class="text-lg font-semibold mb-2">✅ Erfolgreich!</p>
|
|
<p>Die Credential-Response wurde an den Desktop-Browser gesendet.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
|
|
const status = ref('loading')
|
|
const errorMessage = ref('')
|
|
const registrationId = ref('')
|
|
|
|
// Hole registrationId aus URL-Parameter
|
|
onMounted(async () => {
|
|
const route = useRoute()
|
|
registrationId.value = route.query.registrationId || ''
|
|
|
|
if (!registrationId.value) {
|
|
status.value = 'error'
|
|
errorMessage.value = 'Keine registrationId in der URL gefunden.'
|
|
return
|
|
}
|
|
|
|
await startRegistration()
|
|
})
|
|
|
|
async function startRegistration() {
|
|
try {
|
|
status.value = 'loading'
|
|
|
|
// Hole Options vom Server
|
|
console.log('[DEBUG] Fetching options for registrationId:', registrationId.value)
|
|
const optionsResponse = await fetch(`/api/auth/register-passkey-options/${registrationId.value}`, {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
|
|
if (!optionsResponse.ok) {
|
|
throw new Error(`Options request failed: ${optionsResponse.status}`)
|
|
}
|
|
|
|
const optionsData = await optionsResponse.json()
|
|
|
|
if (!optionsData.success || !optionsData.options) {
|
|
throw new Error('Invalid options response')
|
|
}
|
|
|
|
console.log('[DEBUG] Options received:', {
|
|
hasChallenge: !!optionsData.options.challenge,
|
|
rpId: optionsData.options.rp?.id,
|
|
timeout: optionsData.options.timeout
|
|
})
|
|
|
|
// Importiere @simplewebauthn/browser
|
|
const mod = await import('@simplewebauthn/browser')
|
|
|
|
if (!mod.startRegistration) {
|
|
throw new Error('startRegistration ist nicht verfügbar')
|
|
}
|
|
|
|
status.value = 'waiting'
|
|
|
|
console.log('[DEBUG] Calling startRegistration on smartphone...')
|
|
|
|
// Rufe startRegistration auf
|
|
const credential = await mod.startRegistration({ optionsJSON: optionsData.options })
|
|
|
|
console.log('[DEBUG] Credential received:', {
|
|
id: credential.id,
|
|
type: credential.type
|
|
})
|
|
|
|
// Sende Credential-Response an den Server
|
|
const verifyResponse = await fetch('/api/auth/register-passkey', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
registrationId: registrationId.value,
|
|
credential
|
|
})
|
|
})
|
|
|
|
if (!verifyResponse.ok) {
|
|
const errorData = await verifyResponse.json().catch(() => ({}))
|
|
throw new Error(errorData.message || `Verification failed: ${verifyResponse.status}`)
|
|
}
|
|
|
|
const verifyData = await verifyResponse.json()
|
|
|
|
if (verifyData.success) {
|
|
status.value = 'success'
|
|
console.log('[DEBUG] Registration successful!')
|
|
} else {
|
|
throw new Error(verifyData.message || 'Registrierung fehlgeschlagen')
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('[DEBUG] Registration error:', error)
|
|
status.value = 'error'
|
|
errorMessage.value = error.message || 'Unbekannter Fehler'
|
|
}
|
|
}
|
|
|
|
function retry() {
|
|
status.value = 'loading'
|
|
startRegistration()
|
|
}
|
|
</script>
|