Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 44s
Enhance the registration process by adding error handling for the WebAuthn startRegistration method and ensuring the presence of required options. Include debug logging for received options and serialize the options correctly before returning them in the API response, improving robustness and clarity in the registration flow.
98 lines
3.2 KiB
JavaScript
98 lines
3.2 KiB
JavaScript
import crypto from 'crypto'
|
|
import { generateRegistrationOptions } from '@simplewebauthn/server'
|
|
import { readUsers } from '../../utils/auth.js'
|
|
import { getWebAuthnConfig } from '../../utils/webauthn-config.js'
|
|
import { setPreRegistration } from '../../utils/webauthn-challenges.js'
|
|
import { writeAuditLog } from '../../utils/audit-log.js'
|
|
|
|
function isValidEmail(email) {
|
|
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(email || ''))
|
|
}
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const body = await readBody(event)
|
|
const name = String(body?.name || '').trim()
|
|
const email = String(body?.email || '').trim().toLowerCase()
|
|
const phone = String(body?.phone || '').trim()
|
|
|
|
if (!name || !email) {
|
|
throw createError({ statusCode: 400, message: 'Name und E-Mail sind erforderlich' })
|
|
}
|
|
if (!isValidEmail(email)) {
|
|
throw createError({ statusCode: 400, message: 'Ungültige E-Mail-Adresse' })
|
|
}
|
|
|
|
const users = await readUsers()
|
|
if (users.some(u => String(u.email || '').toLowerCase() === email)) {
|
|
throw createError({ statusCode: 409, message: 'Ein Benutzer mit dieser E-Mail-Adresse existiert bereits' })
|
|
}
|
|
|
|
const { rpId, rpName } = getWebAuthnConfig()
|
|
|
|
const userId = crypto.randomUUID()
|
|
const registrationId = crypto.randomBytes(16).toString('hex')
|
|
|
|
const options = await generateRegistrationOptions({
|
|
rpName,
|
|
rpID: rpId,
|
|
userID: new TextEncoder().encode(String(userId)),
|
|
userName: email,
|
|
userDisplayName: name,
|
|
attestationType: 'none',
|
|
authenticatorSelection: {
|
|
residentKey: 'preferred',
|
|
userVerification: 'preferred'
|
|
},
|
|
// Timeout erhöhen für Cross-Device (Standard: 60s, hier: 5 Minuten)
|
|
timeout: 300000
|
|
})
|
|
|
|
setPreRegistration(registrationId, {
|
|
challenge: options.challenge,
|
|
userId,
|
|
name,
|
|
email,
|
|
phone
|
|
})
|
|
|
|
await writeAuditLog('auth.passkey.prereg.options', { email })
|
|
|
|
// CORS-Header für Cross-Device Authentication
|
|
const requestOrigin = getHeader(event, 'origin')
|
|
if (requestOrigin) {
|
|
setHeader(event, 'Access-Control-Allow-Origin', requestOrigin)
|
|
setHeader(event, 'Access-Control-Allow-Credentials', 'true')
|
|
setHeader(event, 'Access-Control-Allow-Methods', 'POST, OPTIONS')
|
|
setHeader(event, 'Access-Control-Allow-Headers', 'Content-Type, Authorization')
|
|
}
|
|
|
|
if (getMethod(event) === 'OPTIONS') {
|
|
return { success: true }
|
|
}
|
|
|
|
// Debug: Log Options-Struktur
|
|
console.log('[WebAuthn Pre-Registration Options]', {
|
|
hasChallenge: !!options.challenge,
|
|
rpId: options.rp?.id,
|
|
userId: options.user?.id ? 'present' : 'missing',
|
|
timeout: options.timeout,
|
|
challengeType: typeof options.challenge
|
|
})
|
|
|
|
// Stelle sicher, dass die Options korrekt serialisiert werden
|
|
// @simplewebauthn/server gibt ein Objekt zurück, das direkt JSON-serialisierbar ist
|
|
// Aber wir müssen sicherstellen, dass alle Properties vorhanden sind
|
|
const serializedOptions = {
|
|
...options,
|
|
challenge: options.challenge,
|
|
rp: options.rp,
|
|
user: options.user,
|
|
pubKeyCredParams: options.pubKeyCredParams,
|
|
authenticatorSelection: options.authenticatorSelection,
|
|
timeout: options.timeout || 300000
|
|
}
|
|
|
|
return { success: true, registrationId, options: serializedOptions }
|
|
})
|
|
|