import { verifyAuthenticationResponse, type AuthenticationResponseJSON } from '@simplewebauthn/server' import { signInUser } from '../../../../utils/auth' import { assertBadRequest, httpError } from '../../../../utils/http' import { getCredentialForVerification, updateCredentialCounter } from '../../../../utils/user-repository' import { requireExistingUser } from '../../../../utils/users' import { consumeLoginChallenge, getWebAuthnConfig, toWebAuthnCredential } from '../../../../utils/webauthn' 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 assertBadRequest(response, 'Passkey login payload is incomplete') assertBadRequest(challengeToken, 'Passkey login payload is incomplete') const expectedChallenge = await consumeLoginChallenge(challengeToken) if (!expectedChallenge) { httpError(400, 'Passkey login challenge expired. Try again.') } const storedCredential = await getCredentialForVerification(response.id) if (!storedCredential) { httpError(401, '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) { httpError(401, 'Passkey authentication failed') } const user = await requireExistingUser(storedCredential.userId, 'User account is not available') if (!user.isActive) { httpError(401, '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 } })