56 lines
1.8 KiB
JavaScript
56 lines
1.8 KiB
JavaScript
import { generateAuthenticationOptions } from '@simplewebauthn/server'
|
|
import { readUsers } from '../../../utils/auth.js'
|
|
import { getWebAuthnConfig } from '../../../utils/webauthn-config.js'
|
|
import { setAuthChallenge } from '../../../utils/webauthn-challenges.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 body = await readBody(event)
|
|
const email = String(body?.email || '').trim().toLowerCase()
|
|
|
|
const { rpId } = getWebAuthnConfig()
|
|
|
|
let allowCredentials
|
|
if (email) {
|
|
const users = await readUsers()
|
|
const user = users.find(u => String(u.email || '').toLowerCase() === email)
|
|
const passkeys = Array.isArray(user?.passkeys) ? user.passkeys : []
|
|
|
|
allowCredentials = passkeys
|
|
.filter(pk => pk?.credentialId)
|
|
.map(pk => ({
|
|
id: pk.credentialId,
|
|
type: 'public-key',
|
|
transports: pk.transports || undefined
|
|
}))
|
|
}
|
|
|
|
// Ohne E-Mail: discoverable Credentials (username-less).
|
|
// Mit E-Mail: allowCredentials nutzen, damit auch nicht-discoverable Credentials funktionieren.
|
|
const options = await generateAuthenticationOptions({
|
|
rpID: rpId,
|
|
allowCredentials,
|
|
userVerification: 'preferred',
|
|
// Timeout erhöhen für Cross-Device (Standard: 60s, hier: 5 Minuten)
|
|
timeout: 300000
|
|
})
|
|
|
|
setAuthChallenge(options.challenge)
|
|
|
|
return { success: true, options }
|
|
})
|
|
|
|
|