Files
harheimertc/server/api/auth/passkeys/registration-options.post.js
Torsten Schulz (local) ad21534862
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 45s
Add CORS support for Cross-Device Authentication in passkey handling
Enhance authentication options in the server API by adding CORS headers to support cross-device authentication. Implement handling for preflight OPTIONS requests and increase timeout for registration and authentication processes to 5 minutes, improving user experience and compatibility across devices.
2026-01-07 20:59:48 +01:00

78 lines
2.7 KiB
JavaScript

import { generateRegistrationOptions } from '@simplewebauthn/server'
import { getUserFromToken, hasAnyRole } from '../../../utils/auth.js'
import { getWebAuthnConfig } from '../../../utils/webauthn-config.js'
import { setRegistrationChallenge } from '../../../utils/webauthn-challenges.js'
import { writeAuditLog } from '../../../utils/audit-log.js'
export default defineEventHandler(async (event) => {
// CORS-Header für Cross-Device Authentication
const origin = getHeader(event, 'origin')
if (origin) {
setHeader(event, 'Access-Control-Allow-Origin', origin)
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 }
}
const token = getCookie(event, 'auth_token')
const user = token ? await getUserFromToken(token) : null
if (!user) {
throw createError({ statusCode: 401, statusMessage: 'Nicht authentifiziert' })
}
// Mindestens für Admin/Vorstand anbieten (und auch für Mitglieder ok)
if (!hasAnyRole(user, 'admin', 'vorstand', 'mitglied', 'newsletter')) {
throw createError({ statusCode: 403, statusMessage: 'Keine Berechtigung' })
}
const { rpId, rpName, origin: webauthnOrigin } = getWebAuthnConfig()
// Debug: Log für Cross-Device Troubleshooting
const requestOrigin = getHeader(event, 'origin')
console.log('[WebAuthn Registration]', {
rpId,
webauthnOrigin,
requestOrigin,
userAgent: getHeader(event, 'user-agent')
})
const existing = Array.isArray(user.passkeys) ? user.passkeys : []
const excludeCredentials = existing
.filter(pk => pk && pk.credentialId)
.map(pk => ({
id: pk.credentialId,
type: 'public-key',
transports: pk.transports || undefined
}))
const options = await generateRegistrationOptions({
rpName,
rpID: rpId,
// @simplewebauthn/server erwartet inzwischen Uint8Array/Buffer statt String
userID: new TextEncoder().encode(String(user.id)),
userName: user.email,
// Keine Attestation-Daten speichern
attestationType: 'none',
authenticatorSelection: {
residentKey: 'preferred',
userVerification: 'preferred'
// authenticatorAttachment weglassen = beide Typen erlauben (platform + cross-platform)
},
excludeCredentials,
// Timeout erhöhen für Cross-Device (Standard: 60s, hier: 5 Minuten)
timeout: 300000
})
setRegistrationChallenge(user.id, options.challenge)
await writeAuditLog('auth.passkey.registration.options', { userId: user.id })
return { success: true, options }
})