Erweitere die Passkey-Registrierung um Unterstützung für bevorzugte Authentifikatortypen und verbessere die Fehlerbehandlung.
All checks were successful
Code Analysis and Production Deploy / analyze (push) Has been skipped
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 1m58s

This commit is contained in:
Torsten Schulz (local)
2026-05-15 13:30:15 +02:00
parent 8ae7dcdbf1
commit 48f8b46e57
2 changed files with 58 additions and 4 deletions

View File

@@ -398,10 +398,53 @@ const addPasskey = async () => {
passkeyLoading.value = true
try {
const name = window.prompt('Name für den Passkey (z.B. "iPhone", "Laptop"):', 'Passkey') || 'Passkey'
const res = await $fetch('/api/auth/passkeys/registration-options', { method: 'POST' })
const mod = await import('@simplewebauthn/browser')
// @simplewebauthn/browser v13+ erwartet { optionsJSON: options }
const credential = await mod.startRegistration({ optionsJSON: res.options })
const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent || '' : ''
const isFirefox = /Firefox\//i.test(userAgent)
let platformAvailable = true
if (typeof mod.platformAuthenticatorIsAvailable === 'function') {
try {
platformAvailable = await mod.platformAuthenticatorIsAvailable()
} catch {
platformAvailable = true
}
}
const preferenceChain = []
if (isFirefox) {
// Firefox kann unter Linux Passkeys browserseitig verwalten, auch wenn kein
// klassischer Platform-Authenticator erkannt wird.
preferenceChain.push('localDevice', 'remoteDevice')
} else if (!platformAvailable) {
preferenceChain.push('remoteDevice', 'localDevice')
} else {
preferenceChain.push('localDevice')
}
let credential = null
let lastError = null
for (const preferredAuthenticatorType of preferenceChain) {
try {
const res = await $fetch('/api/auth/passkeys/registration-options', {
method: 'POST',
body: { preferredAuthenticatorType }
})
// @simplewebauthn/browser v13+ erwartet { optionsJSON: options }
credential = await mod.startRegistration({ optionsJSON: res.options })
break
} catch (err) {
lastError = err
}
}
if (!credential) {
throw lastError || new Error('Passkey konnte nicht erstellt werden.')
}
await $fetch('/api/auth/passkeys/register', {
method: 'POST',
body: { credential, name }
@@ -409,7 +452,12 @@ const addPasskey = async () => {
await loadPasskeys()
successMessage.value = 'Passkey hinzugefügt.'
} catch (e) {
passkeyError.value = e?.data?.message || e?.message || 'Passkey konnte nicht hinzugefügt werden.'
const rawMessage = e?.data?.message || e?.message || ''
if (String(rawMessage).includes('NotAllowedError')) {
passkeyError.value = 'Passkey-Erstellung abgebrochen oder nicht erlaubt. Unter Firefox/Linux ggf. Smartphone-Passkey oder Sicherheitsschlüssel verwenden.'
} else {
passkeyError.value = rawMessage || 'Passkey konnte nicht hinzugefügt werden.'
}
} finally {
passkeyLoading.value = false
}

View File

@@ -18,6 +18,11 @@ export default defineEventHandler(async (event) => {
return { success: true }
}
const body = await readBody(event)
const preferredAuthenticatorType = ['securityKey', 'localDevice', 'remoteDevice'].includes(body?.preferredAuthenticatorType)
? body.preferredAuthenticatorType
: undefined
const token = getCookie(event, 'auth_token')
const user = token ? await getUserFromToken(token) : null
@@ -64,6 +69,7 @@ export default defineEventHandler(async (event) => {
// authenticatorAttachment weglassen = beide Typen erlauben (platform + cross-platform)
},
excludeCredentials,
preferredAuthenticatorType,
// Timeout erhöhen für Cross-Device (Standard: 60s, hier: 5 Minuten)
timeout: 300000
})