Files
harheimertc/server/api/auth/passkeys/register.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

107 lines
3.4 KiB
JavaScript

import { verifyRegistrationResponse } from '@simplewebauthn/server'
import { getUserFromToken, readUsers, writeUsers } from '../../../utils/auth.js'
import { getWebAuthnConfig } from '../../../utils/webauthn-config.js'
import { clearRegistrationChallenge, getRegistrationChallenge } from '../../../utils/webauthn-challenges.js'
import { toBase64Url } from '../../../utils/webauthn-encoding.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' })
}
const body = await readBody(event)
const response = body?.credential
if (!response) {
throw createError({ statusCode: 400, statusMessage: 'Credential fehlt' })
}
const expectedChallenge = getRegistrationChallenge(user.id)
if (!expectedChallenge) {
throw createError({ statusCode: 400, statusMessage: 'Registrierungs-Session abgelaufen. Bitte erneut versuchen.' })
}
const { origin, rpId, requireUV } = getWebAuthnConfig()
let verification
try {
verification = await verifyRegistrationResponse({
response,
expectedChallenge,
expectedOrigin: origin,
expectedRPID: rpId,
requireUserVerification: requireUV
})
} finally {
clearRegistrationChallenge(user.id)
}
const { verified, registrationInfo } = verification
if (!verified || !registrationInfo) {
await writeAuditLog('auth.passkey.registration.failed', { userId: user.id })
throw createError({ statusCode: 400, statusMessage: 'Passkey-Registrierung fehlgeschlagen' })
}
const {
credentialID,
credentialPublicKey,
counter,
credentialDeviceType,
credentialBackedUp
} = registrationInfo
const credentialId = toBase64Url(credentialID)
const publicKey = toBase64Url(credentialPublicKey)
const users = await readUsers()
const idx = users.findIndex(u => u.id === user.id)
if (idx === -1) {
throw createError({ statusCode: 404, statusMessage: 'Benutzer nicht gefunden' })
}
const u = users[idx]
if (!Array.isArray(u.passkeys)) u.passkeys = []
// Duplikate verhindern
if (u.passkeys.some(pk => pk.credentialId === credentialId)) {
return { success: true, message: 'Passkey ist bereits registriert.' }
}
u.passkeys.push({
id: `${Date.now()}`,
credentialId,
publicKey,
counter: Number(counter) || 0,
transports: Array.isArray(response.transports) ? response.transports : undefined,
deviceType: credentialDeviceType,
backedUp: !!credentialBackedUp,
createdAt: new Date().toISOString(),
lastUsedAt: null,
name: body?.name ? String(body.name).slice(0, 80) : 'Passkey'
})
users[idx] = u
await writeUsers(users)
await writeAuditLog('auth.passkey.registered', { userId: user.id })
return { success: true, message: 'Passkey hinzugefügt.' }
})