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

Change Password Bundle Laravel Package

acseo/change-password-bundle

Symfony bundle for managing user password history with FOSUserBundle: stores previous hashed passwords, forces change when passwords are older than 30 days, and optionally blocks reusing old passwords via a validation constraint.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require acseo/change-password-bundle:dev-master
    

    Register the bundle in AppKernel.php:

    new ACSEO\ChangePasswordBundle\ACSEOChangePasswordBundle(),
    
  2. Configure Target User Entity Update config.yml to map your user entity:

    doctrine:
        orm:
            resolve_target_entities:
                FOS\UserBundle\Model\User: AppBundle\Entity\User
    
  3. Update Database Run migrations to create the password_history table:

    php app/console doctrine:schema:update --dump-sql
    php app/console doctrine:schema:update --force
    
  4. First Use Case Trigger a password change via FOSUserBundle’s default flow (/change-password). The bundle will:

    • Automatically log the old password in password_history.
    • Redirect users to /change-password if their password is older than 30 days.

Implementation Patterns

Core Workflows

  1. Password Change Hook Extend FOS\UserBundle\Event\FilterUserResponseEvent to enforce policies:

    // src/AppBundle/EventListener/ChangePasswordListener.php
    class ChangePasswordListener
    {
        public function onChangePassword(FilterUserResponseEvent $event)
        {
            $user = $event->getUser();
            $passwordManager = $this->container->get('fos_user.user_manager');
            $passwordHistory = $this->container->get('acseo_change_password.password_history');
    
            // Custom logic (e.g., block reused passwords)
            if ($passwordHistory->wasPasswordUsed($user, $event->getNewPasswordPlain)) {
                throw new \RuntimeException('Password already used.');
            }
        }
    }
    

    Register in services.yml:

    services:
        app.change_password_listener:
            class: AppBundle\EventListener\ChangePasswordListener
            tags:
                - { name: kernel.event_listener, event: fos_user.change_password.success, method: onChangePassword }
    
  2. Policy Enforcement Override the 30-day redirect threshold in config.yml:

    acseo_change_password:
        password_expiration_days: 60  # Customize expiration
        block_reused_passwords: true  # Enable/disable reuse block
    
  3. Manual Password History Checks Use the service to validate passwords in custom forms:

    $passwordHistory = $this->get('acseo_change_password.password_history');
    if ($passwordHistory->wasPasswordUsed($user, $plainPassword)) {
        $form->addError(new FormError('Password reused.'));
    }
    

Integration Tips

  • Symfony Forms: Add validation constraints in your password field:
    use ACSEO\ChangePasswordBundle\Validator\Constraints\UniquePassword;
    
    $builder->add('plainPassword', PasswordType::class, [
        'constraints' => [
            new UniquePassword(),
        ],
    ]);
    
  • APIs: Use the PasswordHistory service to validate password changes in REST endpoints.
  • FOSUserBundle Extensions: Override UserManager to add pre-save hooks:
    $userManager->setPassword($user, $plainPassword);
    $passwordHistory->logPasswordChange($user, $plainPassword);
    

Gotchas and Tips

Pitfalls

  1. FOSUserBundle Dependency

    • The bundle requires FOS\UserBundle. If missing, password changes will fail silently.
    • Fix: Ensure FOS\UserBundle is installed and configured before enabling this bundle.
  2. Password Hashing Mismatches

    • The bundle compares hashed passwords. If your User entity uses a custom encoder (e.g., bcrypt vs. argon2), ensure the PasswordHistory entity uses the same encoder.
    • Fix: Configure the encoder in services.yml:
      acseo_change_password.password_history:
          class: ACSEO\ChangePasswordBundle\Service\PasswordHistory
          arguments:
              - '@security.password_encoder'
      
  3. Database Schema Conflicts

    • If password_history already exists, --force may corrupt data. Always check --dump-sql first.
    • Fix: Use --dump-sql to review changes before applying.
  4. Caching Issues

    • Password history checks may return stale data if the PasswordHistory service isn’t refreshed after changes.
    • Fix: Clear the cache after manual database updates:
      php app/console cache:clear
      

Debugging

  • Log Password Changes Enable debug logging in config.yml:
    acseo_change_password:
        debug: true  # Logs to `dev.log`
    
  • Check History Manually Query the password_history table directly:
    SELECT * FROM password_history WHERE user_id = [USER_ID];
    
  • Override Expiration Logic Extend the PasswordExpirationChecker service to customize logic:
    // src/AppBundle/Service/CustomExpirationChecker.php
    class CustomExpirationChecker extends \ACSEO\ChangePasswordBundle\Service\PasswordExpirationChecker
    {
        public function isPasswordExpired(UserInterface $user)
        {
            // Custom expiration rules (e.g., role-based)
            return parent::isPasswordExpired($user) && $user->hasRole('ROLE_ADMIN');
        }
    }
    
    Register as a replacement in services.yml:
    services:
        acseo_change_password.password_expiration_checker:
            class: AppBundle\Service\CustomExpirationChecker
    

Extension Points

  1. Custom Validation Add a validator to block passwords based on business rules (e.g., length, complexity):

    // src/AppBundle/Validator/Constraints/PasswordPolicy.php
    class PasswordPolicy extends Constraint
    {
        public $message = 'Password must be 12+ chars with symbols.';
    }
    

    Use with the bundle’s constraints:

    $builder->add('plainPassword', PasswordType::class, [
        'constraints' => [
            new UniquePassword(),
            new PasswordPolicy(),
        ],
    ]);
    
  2. Event-Driven Extensions Listen to acseo_change_password.password.logged to trigger side effects (e.g., notifications):

    // src/AppBundle/EventListener/PasswordLogListener.php
    class PasswordLogListener
    {
        public function onPasswordLogged(PasswordLoggedEvent $event)
        {
            $this->mailer->send('password_changed', [$event->getUser()->getEmail()]);
        }
    }
    

    Register in services.yml:

    services:
        app.password_log_listener:
            class: AppBundle\EventListener\PasswordLogListener
            tags:
                - { name: kernel.event_listener, event: acseo_change_password.password.logged, method: onPasswordLogged }
    
  3. Multi-Tenant Support Add a tenant_id column to password_history and filter queries in the PasswordHistory service:

    public function logPasswordChange(UserInterface $user, $plainPassword)
    {
        $history = new PasswordHistory();
        $history->setUser($user);
        $history->setTenant($this->tenantService->getCurrentTenant());
        $history->setPassword($this->encoder->encodePassword($user, $plainPassword));
        $this->em->persist($history);
        $this->em->flush();
    }
    
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.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui