/** * Password Setup API Endpoint * Handles setting passwords for newly registered users */ import { createKeycloakAdminClient } from '~/server/utils/keycloak-admin'; import { validatePassword } from '~/server/utils/security'; interface SetupPasswordRequest { email: string; password: string; token?: string; } export default defineEventHandler(async (event) => { console.log('[api/auth/setup-password] ========================='); console.log('[api/auth/setup-password] POST /api/auth/setup-password - Password setup'); try { const body = await readBody(event) as SetupPasswordRequest; console.log('[api/auth/setup-password] Setup password attempt for:', body.email); // 1. Validate request data if (!body.email?.trim()) { throw createError({ statusCode: 400, statusMessage: 'Email address is required' }); } if (!body.password?.trim()) { throw createError({ statusCode: 400, statusMessage: 'Password is required' }); } // 2. Validate password strength const passwordValidation = validatePassword(body.password); if (!passwordValidation.isValid) { throw createError({ statusCode: 422, statusMessage: `Password validation failed: ${passwordValidation.errors.join(', ')}` }); } // 3. Find user in Keycloak const keycloakAdmin = createKeycloakAdminClient(); const existingUsers = await keycloakAdmin.findUserByEmail(body.email); if (existingUsers.length === 0) { throw createError({ statusCode: 404, statusMessage: 'User not found. Please register first or contact support.' }); } const user = existingUsers[0]; // 4. Check if user already has a password set by checking if they have any required actions console.log('[api/auth/setup-password] User found:', user.id, 'Required actions:', user.requiredActions); if (user.requiredActions && !user.requiredActions.includes('UPDATE_PASSWORD')) { console.log('[api/auth/setup-password] User already has password set, allowing password update'); // Allow password updates - this could be a password reset scenario } // 5. Set the user's password in Keycloak using direct REST API console.log('[api/auth/setup-password] Setting password for user:', user.id); const adminToken = await keycloakAdmin.getAdminToken(); const config = useRuntimeConfig(); const adminBaseUrl = config.keycloak.issuer.replace('/realms/', '/admin/realms/'); // Set password using Keycloak Admin REST API const setPasswordResponse = await fetch(`${adminBaseUrl}/users/${user.id}/reset-password`, { method: 'PUT', headers: { 'Authorization': `Bearer ${adminToken}`, 'Content-Type': 'application/json', 'User-Agent': 'MonacoUSA-Portal/1.0' }, body: JSON.stringify({ type: 'password', value: body.password, temporary: false }) }); if (!setPasswordResponse.ok) { const errorText = await setPasswordResponse.text().catch(() => 'Unknown error'); throw createError({ statusCode: setPasswordResponse.status, statusMessage: `Failed to set password: ${errorText}` }); } // 6. Update user to ensure they're enabled, email is verified, and remove required actions const updateUserResponse = await fetch(`${adminBaseUrl}/users/${user.id}`, { method: 'PUT', headers: { 'Authorization': `Bearer ${adminToken}`, 'Content-Type': 'application/json', 'User-Agent': 'MonacoUSA-Portal/1.0' }, body: JSON.stringify({ ...user, enabled: true, emailVerified: true, requiredActions: [], // Remove all required actions including UPDATE_PASSWORD attributes: { ...user.attributes, needsPasswordSetup: ['false'], passwordSetAt: [new Date().toISOString()] } }) }); if (!updateUserResponse.ok) { const errorText = await updateUserResponse.text().catch(() => 'Unknown error'); console.warn('[api/auth/setup-password] Failed to update user profile:', errorText); // Don't fail the entire operation if this update fails } console.log(`[api/auth/setup-password] ✅ Password setup successful for user: ${body.email}`); return { success: true, message: 'Password set successfully! You can now log in to your account.', data: { email: body.email, passwordSet: true, canLogin: true } }; } catch (error: any) { console.error('[api/auth/setup-password] ❌ Password setup failed:', error); // Handle Keycloak specific errors if (error.response?.status === 404) { throw createError({ statusCode: 404, statusMessage: 'User not found. Please register first or contact support.' }); } else if (error.response?.status === 409) { throw createError({ statusCode: 409, statusMessage: 'Password has already been set. You can log in with your existing password.' }); } else if (error.response?.status === 400) { throw createError({ statusCode: 422, statusMessage: 'Password does not meet Keycloak security requirements. Please choose a stronger password.' }); } throw error; } });