dcs/password-reset-persistence-orm-bundle
Install the Bundle
Add to composer.json:
"require": {
"dcs/password-reset-persistence-orm-bundle": "^1.0"
}
Run composer update.
Enable the Bundle
Add to config/bundles.php:
return [
// ...
Damianociarla\DCSPasswordResetPersistenceORMBundle\DCSPasswordResetPersistenceORMBundle::class => ['all' => true],
];
Configure Database
Run migrations (if provided) or manually create a password_reset table:
CREATE TABLE password_reset (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL,
token VARCHAR(255) NOT NULL,
expires_at DATETIME NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
First Use Case: Persisting a Reset Request Inject the service and use it in a controller:
use Damianociarla\DCSPasswordResetPersistenceORMBundle\Service\PasswordResetPersistenceService;
public function requestReset(PasswordResetPersistenceService $resetService) {
$resetService->createResetRequest('user@example.com', 'abc123', new \DateTime('+1 hour'));
}
Storing Reset Requests
$resetService->createResetRequest(
$email,
$token, // Unique, secure token (e.g., from Symfony's `PasswordResetTokenGenerator`)
$expiresAt // \DateTimeInterface
);
Fetching a Reset Request
$resetRequest = $resetService->findResetRequest($token);
if ($resetRequest && $resetRequest->getExpiresAt() > new \DateTime()) {
// Valid request
}
Deleting a Used Request
$resetService->deleteResetRequest($token);
Token Generation
Use Symfony’s PasswordResetTokenGenerator to create tokens:
$tokenGenerator = $this->container->get('security.token_generator');
$token = $tokenGenerator->generateToken();
Event Listeners
Listen to ResetPasswordAuthenticationSuccessEvent to auto-delete tokens post-reset:
public function onPasswordResetSuccess(ResetPasswordAuthenticationSuccessEvent $event) {
$resetService->deleteResetRequest($event->getToken());
}
Extend the default PasswordReset entity (if needed):
use Damianociarla\DCSPasswordResetPersistenceORMBundle\Entity\PasswordReset as BasePasswordReset;
class CustomPasswordReset extends BasePasswordReset {
// Add custom fields/methods
}
Update config/packages/dcs_password_reset_persistence_orm.yaml:
dcs_password_reset_persistence_orm:
entity: App\Entity\CustomPasswordReset
Token Collisions
Timezone Issues
expires_at uses the server’s timezone. Normalize with:
$expiresAt = (new \DateTime('+1 hour'))->setTimezone(new \DateTimeZone('UTC'));
Missing Migrations
Race Conditions
findAndDeleteResetRequest($token) (if available) to atomically fetch+delete:
$resetRequest = $resetService->findAndDeleteResetRequest($token);
Check for Stale Tokens Query the DB for expired tokens:
SELECT * FROM password_reset WHERE expires_at < NOW();
Log Token Generation Add a listener to log tokens for debugging:
$resetService->createResetRequest($email, $token, $expiresAt);
$this->logger->debug('Generated token', ['token' => $token]);
Custom Queries Extend the repository to add methods (e.g., find by email):
// src/Repository/CustomPasswordResetRepository.php
public function findByEmail($email) {
return $this->createQueryBuilder('pr')
->andWhere('pr.email = :email')
->setParameter('email', $email)
->getQuery()
->getOneOrNullResult();
}
Event Dispatching
Trigger events (e.g., ResetRequestCreatedEvent) in the service:
$event = new ResetRequestCreatedEvent($resetRequest);
$this->eventDispatcher->dispatch($event, ResetRequestCreatedEvent::NAME);
Rate Limiting
Add a reset_requests table to track attempts per email/IP:
// Pseudocode
if ($this->isRateLimited($email)) {
throw new \RuntimeException('Too many reset requests.');
}
How can I help you explore Laravel packages today?