import { verifyAuthenticationResponse, type AuthenticationResponseJSON } from '@simplewebauthn/server' import { getUserById, getCredentialForVerification, updateCredentialCounter } from '../../../../utils/user-repository' import { consumeLoginChallenge, getWebAuthnConfig, toWebAuthnCredential } from '../../../../utils/webauthn' import { signInUser } from '../../../../utils/auth' export default defineEventHandler(async (event) => { const body = await readBody<{ response?: AuthenticationResponseJSON challengeToken?: string remember?: boolean }>(event) const response = body.response const challengeToken = body.challengeToken?.trim() || '' const remember = body.remember !== false if (!response || !challengeToken) { throw createError({ statusCode: 400, statusMessage: 'Passkey login payload is incomplete' }) } const expectedChallenge = await consumeLoginChallenge(challengeToken) if (!expectedChallenge) { throw createError({ statusCode: 400, statusMessage: 'Passkey login challenge expired. Try again.' }) } const storedCredential = await getCredentialForVerification(response.id) if (!storedCredential) { throw createError({ statusCode: 401, statusMessage: 'Passkey is not recognized' }) } const config = getWebAuthnConfig(event) const verification = await verifyAuthenticationResponse({ response, expectedChallenge, expectedOrigin: config.origin, expectedRPID: config.rpID, credential: toWebAuthnCredential(storedCredential) }) if (!verification.verified) { throw createError({ statusCode: 401, statusMessage: 'Passkey authentication failed' }) } const user = await getUserById(storedCredential.userId) if (!user || !user.isActive) { throw createError({ statusCode: 401, statusMessage: 'User account is not available' }) } await updateCredentialCounter({ credentialId: storedCredential.credentialId, counter: verification.authenticationInfo.newCounter, deviceType: verification.authenticationInfo.credentialDeviceType, backedUp: verification.authenticationInfo.credentialBackedUp }) const authenticatedUser = await signInUser(event, user, remember) return { user: authenticatedUser } })