Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Password History Laravel Package

imanghafoori/laravel-password-history

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require imanghafoori/laravel-password-history
    php artisan vendor:publish --provider="Imanghafoori\PasswordHistory\PasswordHistoryServiceProvider"
    php artisan migrate
    
    • This publishes the config file (config/password_history.php) and creates the password_histories migration table.
  2. Configure Models: Add your User model (or any other model) to the models array in config/password_history.php:

    'models' => [
        \App\Models\User::class,
    ],
    
  3. First Use Case:

    • After installation, the package automatically listens for the saved event on configured models.
    • When a user changes their password (e.g., via Auth::user()->update(['password' => Hash::make($newPassword)])), the old password is stored in the password_histories table.

Implementation Patterns

Core Workflow

  1. Password Change Handling:

    • Extend your password update logic (e.g., in a controller or form request) to ensure the package captures the old password:
      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)
      }
      
    • The package auto-detects password changes via model observers, but explicit calls (e.g., PasswordHistory::record($user)) are useful for custom logic.
  2. Validation:

    • Use the package’s validation rule to enforce password history checks in registration/login:
      use Imanghafoori\PasswordHistory\Rules\PasswordHistory;
      
      $request->validate([
          'password' => ['required', new PasswordHistory],
      ]);
      
    • This rule checks if the new password matches any in the user’s history.
  3. Customizing History Limits:

    • Configure the 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
      
  4. Integration with Auth:

    • Override the 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);
      }
      
  5. Manual Record Management:

    • Manually record passwords for non-standard updates (e.g., admin bulk actions):
      $user->update(['password' => Hash::make('new_password')]);
      PasswordHistory::record($user, false); // Skip auto-detection
      

Gotchas and Tips

Pitfalls

  1. Observer Conflicts:

    • If your model has a custom 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.
    • Fix: Use afterSave or replicating events instead of saved in custom observers.
  2. Password Hashing Mismatches:

    • The package compares plain-text passwords against hashed history entries. If you change your hashing algorithm (e.g., from bcrypt to argon2), history checks may fail.
    • Fix: Rehash all stored passwords in the password_histories table or update the package’s comparison logic.
  3. Case Sensitivity:

    • Password history checks are case-sensitive by default. If your app treats passwords case-insensitively (e.g., Password vs password), history checks may incorrectly block valid changes.
    • Fix: Normalize passwords before comparison:
      PasswordHistory::isPasswordInHistory($user, strtolower($request->password));
      
  4. Performance with Large History:

    • Querying long password histories (e.g., max_history > 100) can slow down password validation.
    • Fix: Add an index to the password column in the password_histories table:
      Schema::table('password_histories', function (Blueprint $table) {
          $table->index('password');
      });
      
  5. Model Caching:

    • If using model caching (e.g., static::$instances), password history checks may return stale data.
    • Fix: Clear the cache after password updates or disable caching for the model.

Debugging Tips

  1. Log History Records:

    • Enable logging in config/password_history.php:
      'log_records' => true,
      
    • Check storage/logs/laravel.log for recorded passwords.
  2. Verify Observer Registration:

    • Confirm the package’s observer is registered:
      php artisan package:discover
      
    • Manually trigger an event to test:
      php artisan tinker
      >>> $user = App\Models\User::first();
      >>> event(new \Illuminate\Database\Eloquent\Model\Event(\Illuminate\Database\Eloquent\Model::class, 'saved', $user));
      
  3. Check Migration:

    • Ensure the password_histories table exists and has the correct columns:
      php artisan migrate:status
      
    • Run php artisan migrate:fresh if the table is missing.

Extension Points

  1. Custom Storage:

    • Override the default 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'));
      }
      
  2. Event-Based Extensions:

    • Listen to the 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,
          ],
      ];
      
  3. Dynamic Model Configuration:

    • Dynamically add/remove models from history tracking at runtime:
      use Imanghafoori\PasswordHistory\Facades\PasswordHistory;
      
      PasswordHistory::addModel(\App\Models\Admin::class);
      PasswordHistory::removeModel(\App\Models\User::class);
      
  4. Custom Validation Logic:

    • Extend the 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);
          }
      }
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle