export default defineEventHandler(async (event) => { try { const { token } = getQuery(event); if (!token || typeof token !== 'string') { console.log('[verify-email] Missing or invalid token'); throw createError({ statusCode: 400, statusMessage: 'Invalid or missing verification token' }); } console.log('[verify-email] Processing verification token...'); // Verify the token WITHOUT consuming it yet const { verifyEmailToken, consumeEmailToken } = await import('~/server/utils/email-tokens'); const { userId, email } = await verifyEmailToken(token); // Update user verification status in Keycloak const { createKeycloakAdminClient } = await import('~/server/utils/keycloak-admin'); const keycloak = createKeycloakAdminClient(); let partialSuccess = false; let keycloakError = null; try { await keycloak.updateUserProfile(userId, { emailVerified: true, attributes: { lastLoginDate: new Date().toISOString() } }); console.log('[verify-email] Successfully verified user:', userId, 'email:', email); // ONLY consume token after successful Keycloak update await consumeEmailToken(token); } catch (keycloakUpdateError: any) { console.error('[verify-email] Keycloak update failed:', keycloakUpdateError.message); // Check if this is a retryable error or a permanent failure if (keycloakUpdateError.message?.includes('error-user-attribute-required')) { // This is a configuration issue - don't consume token, allow retries console.log('[verify-email] Keycloak configuration error - token preserved for retry'); partialSuccess = true; keycloakError = keycloakUpdateError.message; } else { // For other errors, still consume token to prevent infinite retries console.log('[verify-email] Consuming token despite Keycloak error to prevent loops'); await consumeEmailToken(token); partialSuccess = true; keycloakError = keycloakUpdateError.message; } } // Return JSON response with email and success status const responseData = { success: true, message: partialSuccess ? 'Email verified with partial success. You may experience minor account access issues.' : 'Email verified successfully', data: { email, partialSuccess, keycloakError: partialSuccess ? keycloakError : null } }; console.log('[verify-email] Returning JSON response:', responseData); return responseData; } catch (error: any) { console.error('[verify-email] Verification failed:', error.message); // Return appropriate error responses as JSON if (error.message?.includes('expired')) { console.log('[verify-email] Token expired'); throw createError({ statusCode: 410, statusMessage: 'Verification token has expired' }); } else if (error.message?.includes('already used')) { console.log('[verify-email] Token already used'); throw createError({ statusCode: 409, statusMessage: 'Verification token has already been used' }); } else if (error.message?.includes('not found')) { console.log('[verify-email] Token not found'); throw createError({ statusCode: 404, statusMessage: 'Verification token not found or invalid' }); } else { console.log('[verify-email] Generic verification error'); throw createError({ statusCode: 400, statusMessage: error.message || 'Email verification failed' }); } } });