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

Reset Password Bundle Laravel Package

ayto/reset-password-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to First Use

  1. Installation

    composer require ayto/reset-password-bundle
    

    Add to config/bundles.php:

    Ayto\ResetPasswordBundle\ResetPasswordBundle::class => ['all' => true],
    
  2. Configure the Bundle Create config/packages/reset_password.yaml:

    reset_password:
        from_email: 'no-reply@example.com'
        token_lifetime: 3600
        user_class: App\Entity\User
    
  3. Implement ResetPasswordUserInterface Extend your User entity:

    use Ayto\ResetPasswordBundle\Model\ResetPasswordUserInterface;
    
    class User implements ResetPasswordUserInterface {
        // Required methods: getResetPasswordToken(), setResetPasswordToken(),
        // getResetPasswordTokenExpiresAt(), setResetPasswordTokenExpiresAt(), getEmail()
    }
    
  4. Add Routes Include in config/routes.yaml:

    reset_password:
        resource: '@ResetPasswordBundle/Resources/config/routes.yaml'
    
  5. Trigger First Reset Send a test email to a user via:

    php bin/console reset-password:send-email user@example.com
    

    (Check bin/console for available commands.)


First Use Case: User-Initiated Password Reset

  1. User Requests Reset

    • User submits email via /reset-password/request (auto-routed).
    • System generates a secure token, stores it in the User entity, and sends an email.
  2. User Clicks Reset Link

    • Token is validated on /reset-password/reset/{token}.
    • If valid, user is prompted to enter a new password.
  3. Password Update

    • New password is hashed and saved to the User entity.
    • Old token is invalidated.

Implementation Patterns

Workflow: Secure Password Reset with API Platform + JWT

  1. Extend User Entity Ensure your User entity implements ResetPasswordUserInterface and includes:

    #[ORM\Column(nullable: true)]
    private ?string $resetToken = null;
    
    #[ORM\Column(nullable: true)]
    private ?\DateTimeInterface $resetTokenExpiresAt = null;
    
  2. Customize Email Templates Override default templates in:

    templates/bundles/ResetPasswordBundle/emails/reset_password.html.twig
    

    Example:

    {# Extend base template #}
    {% extends '@ResetPasswordBundle/emails/reset_password.html.twig' %}
    
    {# Customize subject/body #}
    {% block subject %}Reset Your Password on {{ app.name }}{% endblock %}
    
  3. Integrate with API Platform Add a PasswordResetController to handle token validation via API:

    #[ApiResource]
    class PasswordResetController extends AbstractController {
        #[Route('/api/reset-password/validate/{token}', name: 'api_reset_password_validate')]
        public function validateToken(string $token): JsonResponse {
            $user = $this->get('reset_password.manager')->findUserByResetToken($token);
            return $this->json(['valid' => $user !== null]);
        }
    }
    
  4. JWT Token Handling After password reset, force JWT token reissue:

    #[Route('/api/reset-password/update', name: 'api_reset_password_update', methods: ['POST'])]
    public function updatePassword(Request $request): JsonResponse {
        $data = json_decode($request->getContent(), true);
        $token = $data['token'];
        $newPassword = $data['password'];
    
        $this->get('reset_password.manager')->resetPassword($token, $newPassword);
        // Log out old JWT sessions (if using stateless auth)
        return $this->json(['message' => 'Password updated. Log in again.']);
    }
    
  5. Event Listeners for Side Effects Listen to ResetPasswordEvents to trigger actions like:

    • Logging password changes.
    • Notifying admins.
    use Ayto\ResetPasswordBundle\Event\PasswordResetEvent;
    
    class PasswordResetLogger {
        public function onPasswordReset(PasswordResetEvent $event) {
            // Log or notify
        }
    }
    

    Register in services.yaml:

    services:
        App\EventListener\PasswordResetLogger:
            tags:
                - { name: 'kernel.event_listener', event: 'reset_password.password_reset', method: 'onPasswordReset' }
    

Integration Tips

  • Doctrine Lifecycle Callbacks Auto-generate tokens on user creation:

    #[ORM\PrePersist]
    public function prePersist(): void {
        $this->resetToken = bin2hex(random_bytes(32));
        $this->resetTokenExpiresAt = new \DateTime('+1 hour');
    }
    
  • Rate Limiting Use Symfony’s RateLimiter to prevent brute-force attacks on /reset-password/request:

    # config/packages/security.yaml
    firewalls:
        main:
            pattern: ^/reset-password
            rate_limiter: reset_password_limiter
    
  • Testing Mock the ResetPasswordManager in PHPUnit:

    $manager = $this->createMock(ResetPasswordManagerInterface::class);
    $manager->method('sendResetEmail')->willReturn(true);
    $this->container->set('reset_password.manager', $manager);
    

Gotchas and Tips

Pitfalls

  1. Token Expiration

    • Tokens expire by default after token_lifetime (3600s). Ensure your frontend handles expired tokens gracefully.
    • Fix: Extend lifetime in config or dynamically set resetTokenExpiresAt in your User entity.
  2. Email Delivery Failures

    • If emails aren’t sent, verify:
      • from_email is valid in reset_password.yaml.
      • Symfony Mailer is configured (check config/packages/mailer.yaml).
    • Debug: Use php bin/console debug:mailer to test email transport.
  3. Token Leaks

    • Tokens are stored in the URL (e.g., /reset-password/reset/{token}). Use POST requests for production to avoid logging.
    • Fix: Redirect users to a form after clicking the email link:
      {# templates/bundles/ResetPasswordBundle/reset_password/reset.html.twig #}
      <form method="POST" action="{{ path('reset_password_reset', { token: token }) }}">
      
  4. User Entity Mismatch

    • If user_class in config doesn’t match your actual User entity, the bundle throws RuntimeException.
    • Fix: Double-check the FQCN in reset_password.yaml.
  5. JWT Session Conflicts

    • After password reset, old JWT tokens remain valid. Implement token invalidation:
      // In your PasswordResetController
      $this->get('lexik_jwt_authentication.jwt_manager')->invalidateToken($oldToken);
      

Debugging

  1. Token Validation Errors

    • Check if resetTokenExpiresAt is in the past or resetToken is missing.
    • Debug Command:
      php bin/console debug:container reset_password.manager
      
      Call findUserByResetToken($token) manually to inspect user data.
  2. Template Overrides Not Working

    • Ensure template paths are correct (e.g., bundles/ResetPasswordBundle/).
    • Fix: Clear cache:
      php bin/console cache:clear
      
  3. Email Not Customized

    • Verify you’ve extended the base template (reset_password.html.twig).
    • Tip: Use Twig’s dump() to debug variables:
      {{ dump(token) }}
      

Extension Points

  1. Custom Token Generator Override the default token generation (32-byte random string):

    // src/Service/CustomTokenGenerator.php
    class CustomTokenGenerator implements TokenGeneratorInterface {
        public function generateToken(): string {
            return base64_encode(random_bytes(24)); // Custom logic
        }
    }
    

    Register as a service:

    services:
        Ayto\ResetPasswordBundle\Service\TokenGenerator:
            class: App\Service\CustomTokenGenerator
    
  2. Additional User Fields Extend ResetPasswordUserInterface to add fields like resetAttempts:

    interface ExtendedResetPasswordUserInterface extends ResetPasswordUserInterface {
        public function getResetAttempts(): int;
        public function incrementResetAttempts(): void;
    }
    

    Update your User entity and bundle config to use the new interface.

  3. Multi-Factor Authentication (MFA) Extend the reset flow to require MFA:

    // In your PasswordResetController
    public function resetPassword
    
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.
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
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament