Pular para o conteúdo principal

Password Validation

The PasswordDefinition class provides comprehensive password strength validation and generation capabilities.

Basic Usage​

Creating a Password Definition​

<?php
use ByJG\Authenticate\Definition\PasswordDefinition;
use ByJG\Authenticate\Model\UserModel;

// Create password definition with default rules
$passwordDefinition = new PasswordDefinition();

// Attach to user model
$userModel = new UserModel();
$userModel->withPasswordDefinition($passwordDefinition);

// Now password is validated when set
$userModel->setPassword('WeakPwd'); // Throws InvalidArgumentException

Applying Rules Through UsersService​

Instead of attaching the definition to every UserModel manually, pass it to the UsersService constructor. Every entity created through the service (e.g., via addUser() or getUsersEntity()) receives the same PasswordDefinition.

<?php
use ByJG\Authenticate\Definition\PasswordDefinition;
use ByJG\Authenticate\Enum\LoginField;
use ByJG\Authenticate\Service\UsersService;

$passwordDefinition = new PasswordDefinition([
PasswordDefinition::MINIMUM_CHARS => 12,
PasswordDefinition::REQUIRE_NUMBERS => 2,
]);

$users = new UsersService(
$usersRepo,
$propsRepo,
LoginField::Username,
$passwordDefinition
);

$newUser = $users->addUser('Jane', 'jane', '[email protected]', 'StrongPassword123!');

Need a blank entity with the validation rules already loaded? Use getUsersEntity():

<?php
$user = $users->getUsersEntity([
'name' => 'John Doe',
'email' => '[email protected]',
]);
$user->setPassword('AnotherStrongPass123!');
$users->save($user);

This approach guarantees that every model created or retrieved through the service (getById(), getByEmail(), getUsersByProperty(), etc.) automatically enforces your password policy.

Password Rules​

Default Rules​

The default password policy requires:

RuleDefault ValueDescription
minimum_chars8Minimum password length
require_uppercase0Number of uppercase letters required
require_lowercase1Number of lowercase letters required
require_symbols0Number of symbols required
require_numbers1Number of digits required
allow_whitespace0Allow whitespace characters (0 = no)
allow_sequential0Allow sequential characters (0 = no)
allow_repeated0Allow repeated patterns (0 = no)

Custom Rules​

<?php
use ByJG\Authenticate\Definition\PasswordDefinition;

$passwordDefinition = new PasswordDefinition([
PasswordDefinition::MINIMUM_CHARS => 12,
PasswordDefinition::REQUIRE_UPPERCASE => 2,
PasswordDefinition::REQUIRE_LOWERCASE => 2,
PasswordDefinition::REQUIRE_SYMBOLS => 1,
PasswordDefinition::REQUIRE_NUMBERS => 2,
PasswordDefinition::ALLOW_WHITESPACE => 0,
PasswordDefinition::ALLOW_SEQUENTIAL => 0,
PasswordDefinition::ALLOW_REPEATED => 0
]);

Setting Individual Rules​

<?php
$passwordDefinition = new PasswordDefinition();
$passwordDefinition->setRule(PasswordDefinition::MINIMUM_CHARS, 10);
$passwordDefinition->setRule(PasswordDefinition::REQUIRE_UPPERCASE, 1);
$passwordDefinition->setRule(PasswordDefinition::REQUIRE_SYMBOLS, 1);

Validating Passwords​

Validation Result Codes​

The matchPassword() method returns a bitwise result:

<?php
$result = $passwordDefinition->matchPassword('weak');

if ($result === PasswordDefinition::SUCCESS) {
echo "Password is valid";
} else {
// Check specific failures
if ($result & PasswordDefinition::FAIL_MINIMUM_CHARS) {
echo "Password is too short\n";
}
if ($result & PasswordDefinition::FAIL_UPPERCASE) {
echo "Missing uppercase letters\n";
}
if ($result & PasswordDefinition::FAIL_LOWERCASE) {
echo "Missing lowercase letters\n";
}
if ($result & PasswordDefinition::FAIL_NUMBERS) {
echo "Missing numbers\n";
}
if ($result & PasswordDefinition::FAIL_SYMBOLS) {
echo "Missing symbols\n";
}
if ($result & PasswordDefinition::FAIL_WHITESPACE) {
echo "Whitespace not allowed\n";
}
if ($result & PasswordDefinition::FAIL_SEQUENTIAL) {
echo "Sequential characters detected\n";
}
if ($result & PasswordDefinition::FAIL_REPEATED) {
echo "Repeated patterns detected\n";
}
}

Available Failure Codes​

ConstantValueDescription
SUCCESS0Password is valid
FAIL_MINIMUM_CHARS1Password too short
FAIL_UPPERCASE2Missing uppercase letters
FAIL_LOWERCASE4Missing lowercase letters
FAIL_SYMBOLS8Missing symbols
FAIL_NUMBERS16Missing numbers
FAIL_WHITESPACE32Whitespace not allowed
FAIL_SEQUENTIAL64Sequential characters detected
FAIL_REPEATED128Repeated patterns detected

Password Generation​

Generate a Random Password​

<?php
$password = $passwordDefinition->generatePassword();
echo $password; // e.g., "aB3dE7fG9"

Generate Longer Passwords​

<?php
// Generate a password 5 characters longer than the minimum
$password = $passwordDefinition->generatePassword(5);

The generated password will:

  • Meet all defined rules
  • Be cryptographically random
  • Avoid sequential and repeated patterns

User Registration with Password Validation​

Complete Example​

<?php
use ByJG\Authenticate\Definition\PasswordDefinition;
use ByJG\Authenticate\Model\UserModel;

// Define password rules
$passwordDefinition = new PasswordDefinition([
PasswordDefinition::MINIMUM_CHARS => 10,
PasswordDefinition::REQUIRE_UPPERCASE => 1,
PasswordDefinition::REQUIRE_LOWERCASE => 1,
PasswordDefinition::REQUIRE_SYMBOLS => 1,
PasswordDefinition::REQUIRE_NUMBERS => 1,
]);

// Create user with password validation
try {
$user = new UserModel();
$user->withPasswordDefinition($passwordDefinition);

$user->setName('John Doe');
$user->setEmail('[email protected]');
$user->setUsername('johndoe');
$user->setPassword($_POST['password']); // Validated automatically

$users->save($user);
echo "User created successfully";

} catch (InvalidArgumentException $e) {
echo "Password validation failed: " . $e->getMessage();
}

User-Friendly Error Messages​

<?php
function getPasswordErrors(int $result): array
{
$errors = [];

if ($result & PasswordDefinition::FAIL_MINIMUM_CHARS) {
$errors[] = "Password must be at least 8 characters long";
}
if ($result & PasswordDefinition::FAIL_UPPERCASE) {
$errors[] = "Password must contain at least one uppercase letter";
}
if ($result & PasswordDefinition::FAIL_LOWERCASE) {
$errors[] = "Password must contain at least one lowercase letter";
}
if ($result & PasswordDefinition::FAIL_NUMBERS) {
$errors[] = "Password must contain at least one number";
}
if ($result & PasswordDefinition::FAIL_SYMBOLS) {
$errors[] = "Password must contain at least one symbol (!@#$%^&*()...)";
}
if ($result & PasswordDefinition::FAIL_WHITESPACE) {
$errors[] = "Password cannot contain whitespace";
}
if ($result & PasswordDefinition::FAIL_SEQUENTIAL) {
$errors[] = "Password cannot contain sequential characters (abc, 123, etc.)";
}
if ($result & PasswordDefinition::FAIL_REPEATED) {
$errors[] = "Password cannot contain repeated patterns";
}

return $errors;
}

// Usage
$result = $passwordDefinition->matchPassword($_POST['password']);
if ($result !== PasswordDefinition::SUCCESS) {
$errors = getPasswordErrors($result);
foreach ($errors as $error) {
echo "- " . $error . "\n";
}
}

Sequential and Repeated Patterns​

Sequential Characters​

Sequential patterns that are detected include:

  • Alphabetic: abc, bcd, cde, xyz, etc. (case-insensitive)
  • Numeric: 012, 123, 234, 789, 890, etc.
  • Reverse: 987, 876, 765, 321, etc.

Repeated Patterns​

Repeated patterns include:

  • Repeated characters: aaa, 111, etc.
  • Repeated sequences: ababab, 123123, etc.

Password Change Flow​

<?php
// Password change with validation
try {
$user = $users->getById($userId);
$user->withPasswordDefinition($passwordDefinition);

// Verify old password
$existingUser = $users->isValidUser($user->getUsername(), $_POST['old_password']);
if ($existingUser === null) {
throw new Exception("Current password is incorrect");
}

// Set new password (validated automatically)
$user->setPassword($_POST['new_password']);
$users->save($user);

echo "Password changed successfully";

} catch (InvalidArgumentException $e) {
echo "New password validation failed: " . $e->getMessage();
}

Best Practices​

  1. Balance security and usability - Don't make rules too restrictive
  2. Educate users - Provide clear error messages
  3. Use password generation - Offer to generate strong passwords
  4. Consider passphrases - Allow longer passwords with spaces if appropriate
  5. Combine with rate limiting - Prevent brute force attacks

Next Steps​