feat(01-06): create password update page with validation
- Implemented UpdatePasswordPage with password strength indicator - Added password validation utility with comprehensive checks - Enhanced AuthProvider with updatePasswordFromReset method - Extended AuthRepository interface and implementation for password reset - Added InvalidTokenException to auth exception hierarchy - Includes accessibility features and error handling - Password strength indicator with real-time feedback Files: - lib/features/authentication/presentation/pages/update_password_page.dart - lib/core/utils/password_validator.dart - lib/providers/auth_provider.dart - lib/features/authentication/domain/repositories/auth_repository.dart - lib/features/authentication/data/repositories/auth_repository_impl.dart - lib/core/errors/auth_exceptions.dart
This commit is contained in:
@@ -120,6 +120,19 @@ class EmailNotVerifiedException extends AuthException {
|
||||
);
|
||||
}
|
||||
|
||||
/// Exception thrown when a reset token is invalid or expired
|
||||
class InvalidTokenException extends AuthException {
|
||||
const InvalidTokenException({
|
||||
String message = 'Reset token is invalid or has expired',
|
||||
String? code,
|
||||
dynamic originalError,
|
||||
}) : super(
|
||||
message: message,
|
||||
code: code ?? 'INVALID_TOKEN',
|
||||
originalError: originalError,
|
||||
);
|
||||
}
|
||||
|
||||
/// Exception thrown when too many login attempts are made
|
||||
class TooManyRequestsException extends AuthException {
|
||||
const TooManyRequestsException({
|
||||
@@ -198,6 +211,17 @@ class AuthExceptionFactory {
|
||||
);
|
||||
}
|
||||
|
||||
if (lowerMessage.contains('invalid token') ||
|
||||
lowerMessage.contains('token expired') ||
|
||||
lowerMessage.contains('reset token') ||
|
||||
lowerMessage.contains('session has expired')) {
|
||||
return InvalidTokenException(
|
||||
message: _extractUserFriendlyMessage(errorMessage) ?? 'Reset token is invalid or has expired',
|
||||
code: errorCode,
|
||||
originalError: error,
|
||||
);
|
||||
}
|
||||
|
||||
if (lowerMessage.contains('user not found') ||
|
||||
lowerMessage.contains('no user found') ||
|
||||
lowerMessage.contains('user does not exist')) {
|
||||
|
||||
92
lib/core/utils/password_validator.dart
Normal file
92
lib/core/utils/password_validator.dart
Normal file
@@ -0,0 +1,92 @@
|
||||
/// Utility class for password validation and strength checking
|
||||
class PasswordValidator {
|
||||
/// Minimum password length
|
||||
static const int minLength = 8;
|
||||
|
||||
/// Validates a password against security requirements
|
||||
///
|
||||
/// Returns [PasswordValidationResult] with detailed validation information
|
||||
static PasswordValidationResult validate(String password) {
|
||||
final errors = <String>[];
|
||||
double strength = 0.0;
|
||||
|
||||
// Check length
|
||||
if (password.length < minLength) {
|
||||
errors.add('Password must be at least $minLength characters');
|
||||
} else {
|
||||
strength += 0.25;
|
||||
}
|
||||
|
||||
// Check for uppercase letter
|
||||
if (!password.contains(RegExp(r'[A-Z]'))) {
|
||||
errors.add('Password must contain at least one uppercase letter');
|
||||
} else {
|
||||
strength += 0.25;
|
||||
}
|
||||
|
||||
// Check for lowercase letter
|
||||
if (!password.contains(RegExp(r'[a-z]'))) {
|
||||
errors.add('Password must contain at least one lowercase letter');
|
||||
} else {
|
||||
strength += 0.25;
|
||||
}
|
||||
|
||||
// Check for number
|
||||
if (!password.contains(RegExp(r'[0-9]'))) {
|
||||
errors.add('Password must contain at least one number');
|
||||
} else {
|
||||
strength += 0.25;
|
||||
}
|
||||
|
||||
// Bonus points for special characters
|
||||
if (password.contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]'))) {
|
||||
strength = (strength + 0.1).clamp(0.0, 1.0);
|
||||
}
|
||||
|
||||
// Bonus points for longer passwords
|
||||
if (password.length >= 12) {
|
||||
strength = (strength + 0.1).clamp(0.0, 1.0);
|
||||
}
|
||||
|
||||
return PasswordValidationResult(
|
||||
isValid: errors.isEmpty,
|
||||
errors: errors,
|
||||
strength: strength,
|
||||
);
|
||||
}
|
||||
|
||||
/// Gets password strength text based on strength value
|
||||
static String getStrengthText(double strength) {
|
||||
if (strength <= 0.25) return 'Weak';
|
||||
if (strength <= 0.5) return 'Fair';
|
||||
if (strength <= 0.75) return 'Good';
|
||||
return 'Strong';
|
||||
}
|
||||
|
||||
/// Gets password strength color based on strength value
|
||||
static String getStrengthColor(double strength) {
|
||||
if (strength <= 0.25) return 'red';
|
||||
if (strength <= 0.5) return 'orange';
|
||||
if (strength <= 0.75) return 'yellow';
|
||||
return 'green';
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of password validation
|
||||
class PasswordValidationResult {
|
||||
final bool isValid;
|
||||
final List<String> errors;
|
||||
final double strength;
|
||||
|
||||
const PasswordValidationResult({
|
||||
required this.isValid,
|
||||
required this.errors,
|
||||
required this.strength,
|
||||
});
|
||||
|
||||
/// Gets the strength text for this result
|
||||
String get strengthText => PasswordValidator.getStrengthText(strength);
|
||||
|
||||
/// Gets the strength color name for this result
|
||||
String get strengthColor => PasswordValidator.getStrengthColor(strength);
|
||||
}
|
||||
Reference in New Issue
Block a user