imanghafoori/laravel-password-history
Installation:
composer require imanghafoori/laravel-password-history
php artisan vendor:publish --provider="Imanghafoori\PasswordHistory\PasswordHistoryServiceProvider"
php artisan migrate
config/password_history.php) and creates the password_histories migration table.Configure Models:
Add your User model (or any other model) to the models array in config/password_history.php:
'models' => [
\App\Models\User::class,
],
First Use Case:
saved event on configured models.Auth::user()->update(['password' => Hash::make($newPassword)])), the old password is stored in the password_histories table.Password Change Handling:
use Imanghafoori\PasswordHistory\PasswordHistory;
public function updatePassword(Request $request) {
$user = Auth::user();
$oldPassword = $user->password; // Store before update
$user->update(['password' => Hash::make($request->password)]);
PasswordHistory::record($user); // Explicitly trigger (optional if auto-observing)
}
PasswordHistory::record($user)) are useful for custom logic.Validation:
use Imanghafoori\PasswordHistory\Rules\PasswordHistory;
$request->validate([
'password' => ['required', new PasswordHistory],
]);
Customizing History Limits:
max_history and min_password_length in config/password_history.php:
'max_history' => 5, // Keep last 5 passwords
'min_password_length' => 8, // Ignore passwords shorter than 8 chars
Integration with Auth:
AuthenticatesUsers trait to add history checks during login:
public function validateCredentials($request) {
$user = $this->getUser();
if ($user && $this->hasValidPassword($request, $user)) {
$this->ensurePasswordNotInHistory($user, $request->password);
return true;
}
return false;
}
protected function ensurePasswordNotInHistory($user, $password) {
return !PasswordHistory::isPasswordInHistory($user, $password);
}
Manual Record Management:
$user->update(['password' => Hash::make('new_password')]);
PasswordHistory::record($user, false); // Skip auto-detection
Observer Conflicts:
saved observer, ensure it doesn’t interfere with the package’s observer. The package uses Laravel’s observing method, which may conflict if multiple observers are registered for the same event.afterSave or replicating events instead of saved in custom observers.Password Hashing Mismatches:
bcrypt to argon2), history checks may fail.password_histories table or update the package’s comparison logic.Case Sensitivity:
Password vs password), history checks may incorrectly block valid changes.PasswordHistory::isPasswordInHistory($user, strtolower($request->password));
Performance with Large History:
max_history > 100) can slow down password validation.password column in the password_histories table:
Schema::table('password_histories', function (Blueprint $table) {
$table->index('password');
});
Model Caching:
static::$instances), password history checks may return stale data.Log History Records:
config/password_history.php:
'log_records' => true,
storage/logs/laravel.log for recorded passwords.Verify Observer Registration:
php artisan package:discover
php artisan tinker
>>> $user = App\Models\User::first();
>>> event(new \Illuminate\Database\Eloquent\Model\Event(\Illuminate\Database\Eloquent\Model::class, 'saved', $user));
Check Migration:
password_histories table exists and has the correct columns:
php artisan migrate:status
php artisan migrate:fresh if the table is missing.Custom Storage:
PasswordHistory class to use a different storage backend (e.g., Redis):
// app/Providers/AppServiceProvider.php
use Imanghafoori\PasswordHistory\PasswordHistory;
public function boot() {
PasswordHistory::setStorage(app('redis'));
}
Event-Based Extensions:
password.history.created event to trigger side effects (e.g., audit logs):
// app/Providers/EventServiceProvider.php
protected $listen = [
\Imanghafoori\PasswordHistory\Events\PasswordHistoryCreated::class => [
\App\Listeners\LogPasswordChange::class,
],
];
Dynamic Model Configuration:
use Imanghafoori\PasswordHistory\Facades\PasswordHistory;
PasswordHistory::addModel(\App\Models\Admin::class);
PasswordHistory::removeModel(\App\Models\User::class);
Custom Validation Logic:
PasswordHistory rule to add business logic (e.g., exclude temporary passwords):
use Imanghafoori\PasswordHistory\Rules\PasswordHistory as BaseRule;
class CustomPasswordHistory extends BaseRule {
public function passes($attribute, $value) {
if (str_starts_with($value, 'temp_')) {
return true; // Skip history check for temp passwords
}
return parent::passes($attribute, $value);
}
}
How can I help you explore Laravel packages today?