124 lines
4.2 KiB
TypeScript
124 lines
4.2 KiB
TypeScript
import { createKeycloakAdminClient } from '~/server/utils/keycloak-admin';
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
console.log('🔄 Forgot password endpoint called at:', new Date().toISOString());
|
|
|
|
try {
|
|
const { email } = await readBody(event);
|
|
|
|
console.log('📧 Password reset request for email:', email ? 'present' : 'missing');
|
|
|
|
// Input validation
|
|
if (!email || typeof email !== 'string') {
|
|
throw createError({
|
|
statusCode: 400,
|
|
statusMessage: 'Email is required'
|
|
});
|
|
}
|
|
|
|
// Basic email validation
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
if (!emailRegex.test(email)) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
statusMessage: 'Please enter a valid email address'
|
|
});
|
|
}
|
|
|
|
const config = useRuntimeConfig() as any;
|
|
|
|
try {
|
|
// Create Keycloak admin client
|
|
const adminClient = createKeycloakAdminClient();
|
|
|
|
console.log('🔧 Using Keycloak admin client for password reset');
|
|
|
|
// Get admin token
|
|
const adminToken = await adminClient.getAdminToken();
|
|
console.log('✅ Admin token obtained');
|
|
|
|
// Find user by email
|
|
const users = await adminClient.findUserByEmail(email, adminToken);
|
|
console.log('🔍 User search result:', { found: users.length > 0 });
|
|
|
|
if (users.length === 0) {
|
|
// For security, don't reveal if email exists or not
|
|
console.log('⚠️ Email not found, but returning success message for security');
|
|
return {
|
|
success: true,
|
|
message: 'If the email exists in our system, a reset link has been sent.'
|
|
};
|
|
}
|
|
|
|
const userId = users[0].id;
|
|
console.log('👤 Found user:', { id: userId, email: users[0].email });
|
|
|
|
// Send password reset email
|
|
await adminClient.sendPasswordResetEmail(
|
|
userId,
|
|
adminToken,
|
|
config.keycloak.clientId,
|
|
config.keycloak.callbackUrl
|
|
);
|
|
|
|
console.log('✅ Password reset email sent successfully');
|
|
|
|
return {
|
|
success: true,
|
|
message: 'If the email exists in our system, a reset link has been sent.'
|
|
};
|
|
|
|
} catch (keycloakError: any) {
|
|
console.error('❌ Keycloak API error:', keycloakError);
|
|
|
|
// Handle timeout errors specifically
|
|
if (keycloakError.name === 'AbortError') {
|
|
console.error('⏰ Password reset request timed out after 30 seconds');
|
|
return {
|
|
success: true,
|
|
message: 'Password reset request is being processed. If the email exists in our system, a reset link will be sent shortly.'
|
|
};
|
|
}
|
|
|
|
// Handle SMTP/email server errors
|
|
if (keycloakError.message?.includes('send reset email') || keycloakError.message?.includes('SMTP') || keycloakError.message?.includes('500')) {
|
|
console.error('📧 Email server error detected, but user search was successful');
|
|
return {
|
|
success: true,
|
|
message: 'If the email exists in our system, a reset link has been sent. If you don\'t receive an email, please contact your administrator.'
|
|
};
|
|
}
|
|
|
|
// Handle permission errors
|
|
if (keycloakError.message?.includes('403') || keycloakError.message?.includes('Forbidden')) {
|
|
console.error('🔒 Permission error detected - admin client may not have proper roles');
|
|
console.error('💡 Suggestion: Check that admin-cli client has view-users and manage-users roles');
|
|
return {
|
|
success: true,
|
|
message: 'Password reset service is temporarily unavailable. Please contact your administrator.'
|
|
};
|
|
}
|
|
|
|
// For security, don't reveal specific errors to the user
|
|
return {
|
|
success: true,
|
|
message: 'If the email exists in our system, a reset link has been sent.'
|
|
};
|
|
}
|
|
|
|
} catch (error: any) {
|
|
console.error('❌ Forgot password error:', error);
|
|
|
|
// If it's already a createError, just throw it
|
|
if (error.statusCode) {
|
|
throw error;
|
|
}
|
|
|
|
// Generic error for unexpected issues
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: 'Failed to process password reset request. Please try again.'
|
|
});
|
|
}
|
|
});
|