Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | 1x 14x 7x 7x 7x 2x 5x 5x 5x 2x 1x 1x 1x 1x | /**
* useForgotPassword Hook - Amplify v6 Direct Integration
* Story 1.2.2: Implement Forgot Password Flow (UPDATED October 2025)
*
* ARCHITECTURE CHANGE: Migrated from backend API to Amplify v6 direct integration
* OLD: Frontend → Backend API → Cognito
* NEW: Frontend → Amplify v6 → Cognito (direct)
*/
import { useMutation } from '@tanstack/react-query';
import { resetPassword } from 'aws-amplify/auth';
import { ensureAmplifyConfigured } from '@/config/amplify';
interface UseForgotPasswordOptions {
onSuccess?: () => void;
onError?: (error: ForgotPasswordError) => void;
}
// Extended Error type to include rate limit info
export interface ForgotPasswordError extends Error {
type: 'rateLimitExceeded' | 'userNotFound' | 'invalidParameter' | 'unknownError' | 'networkError';
statusCode?: number;
}
export const useForgotPassword = (options?: UseForgotPasswordOptions) => {
return useMutation<void, ForgotPasswordError, string>({
mutationFn: async (email: string) => {
try {
// Amplify is configured lazily (perf/public-homepage-followup #2) — ensure before use.
await ensureAmplifyConfigured();
// Amplify v6: resetPassword sends code to user's email automatically
const result = await resetPassword({ username: email });
console.log('[useForgotPassword] Password reset code sent:', result);
// Cognito automatically sends email with 6-digit code
// User will enter code in Story 1.2.2a confirmation screen
} catch (error: unknown) {
console.error('[useForgotPassword] Password reset failed:', error);
const errorCode =
(error as { code?: string; name?: string }).code ||
(error as { code?: string; name?: string }).name ||
'UNKNOWN_ERROR';
// Map Cognito errors to app errors
switch (errorCode) {
case 'LimitExceededException':
throw Object.assign(new Error('Too many password reset attempts'), {
type: 'rateLimitExceeded' as const,
statusCode: 429,
});
case 'UserNotFoundException':
// Email enumeration prevention: Don't throw, return success
console.log('[useForgotPassword] User not found (suppressed for security)');
return; // Success (enumeration prevention)
case 'InvalidParameterException':
throw Object.assign(new Error('Invalid email address'), {
type: 'invalidParameter' as const,
statusCode: 400,
});
default:
throw Object.assign(
new Error((error as { message?: string }).message || 'Password reset failed'),
{
type: 'unknownError' as const,
statusCode: 500,
}
);
}
}
},
onSuccess: options?.onSuccess,
onError: options?.onError,
});
};
|