export default defineEventHandler(async (event) => { try { const { token } = getQuery(event); if (!token || typeof token !== 'string') { console.log('[verify-email] Missing or invalid token, redirecting to expired page'); return sendRedirect(event, '/auth/verify-expired?reason=invalid', 302); } 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; } } // Build success redirect URL with query parameters const successUrl = new URL('/auth/verify-success', 'https://portal.monacousa.org'); successUrl.searchParams.set('email', email); if (partialSuccess && keycloakError) { successUrl.searchParams.set('warning', 'partial'); console.log('[verify-email] Redirecting to success page with partial warning'); } else { console.log('[verify-email] Redirecting to success page - verification complete'); } // Redirect to success page instead of returning JSON return sendRedirect(event, successUrl.pathname + successUrl.search, 302); } catch (error: any) { console.error('[verify-email] Verification failed:', error.message); // Redirect to appropriate error page instead of throwing errors if (error.message?.includes('expired')) { console.log('[verify-email] Token expired, redirecting to expired page'); return sendRedirect(event, '/auth/verify-expired?reason=expired', 302); } else if (error.message?.includes('already used')) { console.log('[verify-email] Token already used, redirecting to expired page'); return sendRedirect(event, '/auth/verify-expired?reason=used', 302); } else if (error.message?.includes('not found')) { console.log('[verify-email] Token not found, redirecting to expired page'); return sendRedirect(event, '/auth/verify-expired?reason=invalid', 302); } else { console.log('[verify-email] Generic verification error, redirecting to expired page'); return sendRedirect(event, '/auth/verify-expired?reason=invalid', 302); } } });