### Install and Register Bundle Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Commands and configuration to install the bundle via Composer and register it within the Symfony kernel. ```bash composer require tilleuls/forgot-password-bundle ``` ```php // config/bundles.php return [ // ... CoopTilleuls\ForgotPasswordBundle\CoopTilleulsForgotPasswordBundle::class => ['all' => true], ]; ``` -------------------------------- ### Install CoopTilleulsForgotPasswordBundle Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/index.md Use Composer to add the bundle to your Symfony project dependencies. ```shell composer require tilleuls/forgot-password-bundle ``` -------------------------------- ### Specify Provider via HTTP Header Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/index.md Example of how to specify a custom provider when making a request to the password recovery endpoint. ```http POST /forgot-password/ FP-provider= ``` -------------------------------- ### Handle UserNotFoundEvent for Logging Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Implements custom logic for when a password reset is requested for a non-existent user. This example logs the failed attempt for security monitoring purposes, using PSR-3 logger. It adheres to security best practices by not revealing user existence. ```php namespace App\EventSubscriber; use CoopTilleuls\ForgotPasswordBundle\Event\UserNotFoundEvent; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; final class ForgotPasswordEventSubscriber implements EventSubscriberInterface { public function __construct(private readonly LoggerInterface $logger) { } public static function getSubscribedEvents(): array { return [ UserNotFoundEvent::class => 'onUserNotFound', ]; } public function onUserNotFound(UserNotFoundEvent $event): void { $context = $event->getContext(); // Log the failed attempt for security monitoring $this->logger->warning('Password reset requested for unknown user', [ 'email' => $context['email'] ?? 'unknown', 'ip' => $context['ip'] ?? 'unknown', 'timestamp' => (new \DateTime())->format('c'), ]); // Note: Do not reveal that the user doesn't exist (security best practice) // The API already returns 204 No Content for both cases } } ``` -------------------------------- ### Validate Password Reset Token via REST API Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Retrieves token information using a GET request. Returns 200 OK with token data or 404 if invalid. ```bash curl -X GET https://api.example.com/forgot-password/abc123def456 \ -H "Accept: application/json" ``` -------------------------------- ### Override Token Retrieval Route Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/index.md Provides instructions on how to override the default GET /forgot-password/{tokenValue} route by defining a custom route and controller to return a specific response. ```yaml coop_tilleuls_forgot_password.get_token: path: /forgot-password/{tokenValue} methods: [ GET ] defaults: _controller: App\Controller\GetTokenController ``` ```php namespace App\Controller; use Symfony\Component\HttpFoundation\Response; final class GetTokenController { public function __invoke(): Response { return new Response('', Response::HTTP_NO_CONTENT); } } ``` -------------------------------- ### Override GET Token Response Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Override the default GET /forgot-password/{tokenValue} endpoint to return a custom response format or an empty response. This is achieved by defining a custom controller and updating the routing configuration. ```yaml # config/routes/coop_tilleuls_forgot_password.yaml coop_tilleuls_forgot_password: resource: . type: coop_tilleuls_forgot_password prefix: '/forgot-password' # Override the get token route coop_tilleuls_forgot_password.get_token: path: /forgot-password/{tokenValue} methods: [GET] defaults: _controller: App\Controller\GetTokenController ``` ```php // src/Controller/GetTokenController.php namespace App\Controller; use CoopTilleuls\ForgotPasswordBundle\Entity\AbstractPasswordToken; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; final class GetTokenController { public function __invoke(AbstractPasswordToken $token): Response { // Option 1: Return empty response (just validate token exists) return new Response('', Response::HTTP_NO_CONTENT); // Option 2: Return custom JSON structure return new JsonResponse([ 'valid' => true, 'expires_in' => $token->getExpiresAt()->getTimestamp() - time(), ]); } } ``` -------------------------------- ### Override GET Token Response Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Override the default GET /forgot-password/{tokenValue} endpoint to return a custom response format or an empty response, allowing for custom validation logic or response structures. ```APIDOC ## Override GET Token Response Override the default GET `/forgot-password/{tokenValue}` endpoint to return a custom response format or empty response. ```yaml # config/routes/coop_tilleuls_forgot_password.yaml coop_tilleuls_forgot_password: resource: . type: coop_tilleuls_forgot_password prefix: '/forgot-password' # Override the get token route coop_tilleuls_forgot_password.get_token: path: /forgot-password/{tokenValue} methods: [GET] defaults: _controller: App\Controller\GetTokenController ``` ```php // src/Controller/GetTokenController.php namespace App\Controller; use CoopTilleuls\ForgotPasswordBundle\Entity\AbstractPasswordToken; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; final class GetTokenController { public function __invoke(AbstractPasswordToken $token): Response { // Option 1: Return empty response (just validate token exists) return new Response('', Response::HTTP_NO_CONTENT); // Option 2: Return custom JSON structure return new JsonResponse([ 'valid' => true, 'expires_in' => $token->getExpiresAt()->getTimestamp() - time(), ]); } } ``` ``` -------------------------------- ### Configure Security for Forgot Password Endpoints Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Configures Symfony's security.yaml file to allow anonymous access to all routes starting with '/forgot-password'. This is a crucial step to ensure users can initiate the password reset process without being authenticated. ```yaml # config/packages/security.yaml security: firewalls: main: pattern: ^/ lazy: true # ... your authentication configuration access_control: # Allow anonymous access to forgot password endpoints - { path: '^/forgot-password', role: PUBLIC_ACCESS } # ... other access control rules ``` -------------------------------- ### Configure Multiple User Providers Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/index.md Demonstrates how to configure the bundle to support multiple user providers, such as customers and admins, each with their own specific token and user entity configurations. ```yaml coop_tilleuls_forgot_password: use_jms_serializer: false providers: customer: default: true password_token: class: 'App\Security\Entity\PasswordToken' expires_in: '1 day' user_field: 'user' serialization_groups: [ ] user: class: 'App\Security\Entity\User' email_field: 'email' password_field: 'password' authorized_fields: [ 'email' ] admin: password_token: class: 'App\Security\Entity\PasswordAdminToken' expires_in: '4 hours' user_field: 'user' serialization_groups: [ ] user: class: 'App\Security\Entity\Admin' email_field: 'username' password_field: 'password' authorized_fields: [ 'email' ] ``` -------------------------------- ### Define Bundle Configuration Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Configure the password token and user entities, including field mappings and token expiration settings. ```yaml # config/packages/coop_tilleuls_forgot_password.yaml coop_tilleuls_forgot_password: password_token: class: 'App\Entity\PasswordToken' expires_in: '1 day' user_field: 'user' user: class: 'App\Entity\User' email_field: 'email' password_field: 'password' authorized_fields: ['email'] ``` -------------------------------- ### Implement Custom Manager Interface Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/use_custom_manager.md Create a custom manager class by implementing the ManagerInterface. This allows the bundle to interact with custom storage systems for finding, persisting, and removing password tokens. ```php namespace App\Manager; use App\Entity\PasswordToken; use CoopTilleuls\ForgotPasswordBundle\Manager\Bridge\ManagerInterface; final class FooManager implements ManagerInterface { public function findOneBy($class, array $criteria): ?PasswordToken { // Find & return an object of a specific class according to criteria } public function persist($object): void { // Save PasswordToken object } public function remove($object): void { // Remove PasswordToken object } } ``` -------------------------------- ### Implement Custom Token Generator Interface (PHP) Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/use_custom_token_generator.md This snippet demonstrates how to create a custom token generator service by implementing the TokenGeneratorInterface. The `generate` method should return the custom token as a string. This allows for flexible token creation logic. ```php namespace App\TokenGenerator; use CoopTilleuls\ForgotPasswordBundle\TokenGenerator\TokenGeneratorInterface; final class FooTokenGenerator implements TokenGeneratorInterface { public function generate(): string { // generate your own token and return it as string } } ``` -------------------------------- ### Configure Basic Bundle Settings Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/index.md Defines the required entity classes and field mappings for password tokens and users. This configuration is essential for the bundle to identify how to handle password reset requests. ```yaml coop_tilleuls_forgot_password: password_token: class: 'App\Entity\PasswordToken' expires_in: '1 day' user_field: 'user' serialization_groups: [ ] user: class: 'App\Entity\User' email_field: 'email' password_field: 'password' authorized_fields: [ 'email' ] use_jms_serializer: false ``` -------------------------------- ### Configure Multiple User Providers Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Set up distinct password reset configurations for different user types, such as customers and administrators. ```yaml coop_tilleuls_forgot_password: providers: customer: default: true password_token: { class: 'App\Entity\PasswordToken', expires_in: '1 day' } user: { class: 'App\Entity\User', email_field: 'email' } admin: password_token: { class: 'App\Entity\PasswordAdminToken', expires_in: '4 hours' } user: { class: 'App\Entity\Admin', email_field: 'username' } ``` -------------------------------- ### Implement UserNotFoundEvent Subscriber Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/usage.md Shows how to listen for the UserNotFoundEvent to execute custom business logic when a user is not found during the password recovery process. ```php namespace App\EventSubscriber; use CoopTilleuls\ForgotPasswordBundle\Event\UserNotFoundEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; final class ForgotPasswordEventSubscriber implements EventSubscriberInterface { public function __construct(private readonly UserManager $userManager) {} public static function getSubscribedEvents(): array { return [ UserNotFoundEvent::class => 'onUserNotFound', ]; } public function onUserNotFound(UserNotFoundEvent $event): void { $context = $event->getContext(); } } ``` -------------------------------- ### Configure Custom Token Generator (YAML) Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/use_custom_token_generator.md This snippet shows how to update the bundle's configuration to use your custom token generator service. By specifying the fully qualified class name of your generator, the bundle will use it for token creation. ```yaml coop_tilleuls_forgot_password: # ... token_generator: 'App\TokenGenerator\FooTokenGenerator' ``` -------------------------------- ### POST /forgot-password/ Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/usage.md Initiates the password reset process by sending an identifier to trigger a token creation. ```APIDOC ## POST /forgot-password/ ### Description Initiates the password reset process. Dispatches the `coop_tilleuls_forgot_password.create_token` event upon success or `coop_tilleuls_forgot_password.user_not_found` if the user is not found. ### Method POST ### Endpoint /forgot-password/ ### Parameters #### Request Body - **email** (string) - Required - The email address of the user requesting the password reset. ### Request Example { "email": "foo@example.com" } ### Response #### Success Response (200) - **status** (string) - Confirmation that the reset process has been initiated. ``` -------------------------------- ### POST /forgot-password/ Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/index.md Requests a new password for a user. Dispatches the `coop_tilleuls_forgot_password.create_token` event. ```APIDOC ## POST /forgot-password/ ### Description Requests a new password for a user. This endpoint dispatches the `coop_tilleuls_forgot_password.create_token` event. ### Method POST ### Endpoint /forgot-password/ ### Parameters #### Query Parameters - **FP-provider** (string) - Optional - Specifies the provider to use for password management. #### Request Body - **replace_with_your_email_field** (string) - Required - The email address of the user requesting a password reset. ### Request Example ```json { "replace_with_your_email_field": "johndoe@example.com" } ``` ### Response #### Success Response (200) - **message** (string) - Confirmation message that the password reset request has been processed. #### Response Example ```json { "message": "Password reset email sent successfully." } ``` ``` -------------------------------- ### Configure Custom Manager Service Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/use_custom_manager.md Register the custom manager service in the bundle configuration file to override the default Doctrine implementation. ```yaml coop_tilleuls_forgot_password: manager: 'App\Manager\FooManager' ``` -------------------------------- ### Create PasswordToken Entity Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/index.md Define a custom PasswordToken entity by extending the AbstractPasswordToken provided by the bundle. ```php // src/Entity/PasswordToken.php namespace App\Entity; use CoopTilleuls\ForgotPasswordBundle\Entity\AbstractPasswordToken; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] class PasswordToken extends AbstractPasswordToken { #[ORM\Id] #[ORM\Column(type: 'integer', nullable: false)] #[ORM\GeneratedValue(strategy: 'AUTO')] private ?int $id = null; #[ORM\ManyToOne(targetEntity: User::class)] #[ORM\JoinColumn(nullable: false)] private ?User $user = null; public function getId(): ?int { return $this->id; } public function getUser(): ?User { return $this->user; } /** * @param User $user */ public function setUser($user): self { $this->user = $user; return $this; } } ``` -------------------------------- ### Handle Password Update Validation Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/usage.md Demonstrates how to validate a user object during the password update event using the validator interface. ```php public function onUpdatePassword(UpdatePasswordEvent $event): void { $passwordToken = $event->getPasswordToken(); $user = $passwordToken->getUser(); $user->setPlainPassword($event->getPassword()); $this->validator->validate($user); $this->userManager->updateUser($user); } ``` -------------------------------- ### Custom Manager Implementation Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Implement the ManagerInterface to use a custom persistence layer (e.g., MongoDB, Redis, or an external API) instead of Doctrine ORM. ```APIDOC ## Custom Manager Interface Implement `ManagerInterface` to use a custom persistence layer instead of Doctrine ORM (e.g., MongoDB, Redis, or external API). ```php // src/Manager/CustomManager.php namespace App\Manager; use App\Entity\PasswordToken; use CoopTilleuls\ForgotPasswordBundle\Manager\Bridge\ManagerInterface; final class CustomManager implements ManagerInterface { public function __construct(private readonly SomeStorageService $storage) { } /** * Find an entity by class and criteria */ public function findOneBy($class, array $criteria): ?object { // Example: Find a user or token by criteria return $this->storage->findOne($class, $criteria); } /** * Persist an entity (save to storage) */ public function persist($object): void { $this->storage->save($object); } /** * Remove an entity from storage */ public function remove($object): void { $this->storage->delete($object); } } ``` ```yaml # config/packages/coop_tilleuls_forgot_password.yaml coop_tilleuls_forgot_password: password_token: class: 'App\Entity\PasswordToken' user: class: 'App\Entity\User' manager: 'App\Manager\CustomManager' ``` ``` -------------------------------- ### Request Password Reset Token via REST API Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Sends a POST request to initiate a password reset. Supports specifying a provider via the FP-provider header. ```bash curl -X POST https://api.example.com/forgot-password/ \ -H "Content-Type: application/json" \ -d '{"email": "user@example.com"}' curl -X POST https://api.example.com/forgot-password/ \ -H "Content-Type: application/json" \ -H "FP-provider: admin" \ -d '{"username": "admin@example.com"}' ``` -------------------------------- ### Implement Custom Manager Interface Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Implement ManagerInterface to use a custom persistence layer (e.g., MongoDB, Redis, or external API) instead of Doctrine ORM. This involves defining findOneBy, persist, and remove methods to interact with your chosen storage. ```php namespace App\Manager; use App\Entity\PasswordToken; use CoopTilleuls\ForgotPasswordBundle\Manager\Bridge\ManagerInterface; final class CustomManager implements ManagerInterface { public function __construct(private readonly SomeStorageService $storage) { } /** * Find an entity by class and criteria */ public function findOneBy($class, array $criteria): ?object { // Example: Find a user or token by criteria return $this->storage->findOne($class, $criteria); } /** * Persist an entity (save to storage) */ public function persist($object): void { $this->storage->save($object); } /** * Remove an entity from storage */ public function remove($object): void { $this->storage->delete($object); } } ``` ```yaml # config/packages/coop_tilleuls_forgot_password.yaml coop_tilleuls_forgot_password: password_token: class: 'App\Entity\PasswordToken' user: class: 'App\Entity\User' manager: 'App\Manager\CustomManager' ``` -------------------------------- ### Configure Routing for Password Recovery Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/index.md Define the routes for the bundle in your application configuration to handle password reset requests. ```yaml # config/routes/coop_tilleuls_forgot_password.yaml coop_tilleuls_forgot_password: resource: . type: coop_tilleuls_forgot_password prefix: '/forgot-password' ``` -------------------------------- ### Register Bundle in Symfony Kernel Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/index.md Add the bundle class to the bundles configuration file to enable it in the application. ```php // config/bundles.php return [ // ... CoopTilleuls\ForgotPasswordBundle\CoopTilleulsForgotPasswordBundle::class => ['all' => true], ]; ``` -------------------------------- ### Encode and Update User Password (PHP) Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/usage.md This subscriber handles the `UpdatePasswordEvent`, which is dispatched after a user has reset their password. It encodes the new password using a UserManager and saves it to the user entity. This ensures the password is securely stored. ```php namespace App\EventSubscriber; // ... use CoopTilleuls\ForgotPasswordBundle\Event\UpdatePasswordEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; final class ForgotPasswordEventSubscriber implements EventSubscriberInterface { public function __construct(private readonly UserManager $userManager) { } public static function getSubscribedEvents(): array { return [ // Symfony 4.3 and inferior, use 'coop_tilleuls_forgot_password.update_password' event name UpdatePasswordEvent::class => 'onUpdatePassword', ]; } public function onUpdatePassword(UpdatePasswordEvent $event): void { $passwordToken = $event->getPasswordToken(); $user = $passwordToken->getUser(); $user->setPlainPassword($event->getPassword()); $this->userManager->updateUser($user); } } ``` -------------------------------- ### Custom Token Generator Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Implement the TokenGeneratorInterface to create custom token formats, such as UUIDs, shorter codes, or using cryptographically different algorithms. ```APIDOC ## Custom Token Generator Implement `TokenGeneratorInterface` to create custom token formats (e.g., UUID, shorter codes, or cryptographically different algorithms). ```php // src/TokenGenerator/UuidTokenGenerator.php namespace App\TokenGenerator; use CoopTilleuls\ForgotPasswordBundle\TokenGenerator\TokenGeneratorInterface; use Symfony\Component\Uid\Uuid; final class UuidTokenGenerator implements TokenGeneratorInterface { public function generate(): string { // Generate a UUID v4 token return Uuid::v4()->toRfc4122(); } } // Alternative: Short numeric code generator final class ShortCodeTokenGenerator implements TokenGeneratorInterface { public function generate(): string { // Generate a 6-digit numeric code return str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT); } } ``` ```yaml # config/packages/coop_tilleuls_forgot_password.yaml coop_tilleuls_forgot_password: password_token: class: 'App\Entity\PasswordToken' user: class: 'App\Entity\User' token_generator: 'App\TokenGenerator\UuidTokenGenerator' ``` ``` -------------------------------- ### Create EventSubscriber for Password Reset Security Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/user_not_authenticated.md This EventSubscriber listens to the kernel.request event to check if the current route belongs to the forgot password bundle. If the user is already authenticated, it blocks access by throwing an AccessDeniedHttpException. ```php namespace App\EventSubscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; final class ForgotPasswordEventSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents(): array { return [ KernelEvents::REQUEST => 'onKernelRequest', ]; } public function onKernelRequest(RequestEvent $event): void { if (!$event->isMainRequest() || !str_starts_with($event->getRequest()->get('_route'), 'coop_tilleuls_forgot_password')) { return; } $token = $this->tokenStorage->getToken(); if (null !== $token && $token->getUser() instanceof UserInterface) { throw new AccessDeniedHttpException; } } } ``` -------------------------------- ### AbstractPasswordToken Methods Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt The AbstractPasswordToken class provides core token functionality. Extend this class for your PasswordToken entity and implement the required abstract methods. ```APIDOC ## AbstractPasswordToken Methods The `AbstractPasswordToken` class provides core token functionality. Extend this class for your `PasswordToken` entity. ```php use CoopTilleuls\ForgotPasswordBundle\Entity\AbstractPasswordToken; // AbstractPasswordToken provides these methods: $token->getToken(); // Returns the token string $token->setToken(string $token); // Sets the token string $token->getExpiresAt(); // Returns DateTime of expiration $token->setExpiresAt(\DateTime $date); // Sets expiration date $token->isExpired(); // Returns true if token has expired // Your implementation must define: abstract public function getId(); // Return the entity ID abstract public function getUser(); // Return the associated user abstract public function setUser($user); // Set the associated user ``` ``` -------------------------------- ### POST /forgot-password/ (User Not Found) Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/index.md Handles cases where the user is not found during a password request. Dispatches the `coop_tilleuls_forgot_password.user_not_found` event. ```APIDOC ## POST /forgot-password/ (User Not Found) ### Description Handles cases where the user is not found during a password request. This endpoint dispatches the `coop_tilleuls_forgot_password.user_not_found` event. ### Method POST ### Endpoint /forgot-password/ ### Parameters #### Query Parameters - **FP-provider** (string) - Optional - Specifies the provider to use for password management. #### Request Body - **replace_with_your_email_field** (string) - Required - The email address of the user. ### Request Example ```json { "replace_with_your_email_field": "nonexistent@example.com" } ``` ### Response #### Success Response (200) - **message** (string) - Message indicating that the user was not found. #### Response Example ```json { "message": "User not found." } ``` ``` -------------------------------- ### Handle CreateTokenEvent for Email Notification Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Subscribes to the CreateTokenEvent to send a password reset email to the user. Uses Symfony Mailer and Twig templates. ```php namespace App\EventSubscriber; use CoopTilleuls\ForgotPasswordBundle\Event\CreateTokenEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mime\Email; use Twig\Environment; final class ForgotPasswordEventSubscriber implements EventSubscriberInterface { public function __construct( private readonly MailerInterface $mailer, private readonly Environment $twig ) {} public static function getSubscribedEvents(): array { return [CreateTokenEvent::class => 'onCreateToken']; } public function onCreateToken(CreateTokenEvent $event): void { $passwordToken = $event->getPasswordToken(); $user = $passwordToken->getUser(); $message = (new Email()) ->from('no-reply@example.com') ->to($user->getEmail()) ->subject('Reset your password') ->html($this->twig->render('emails/reset_password.html.twig', [ 'reset_password_url' => sprintf('https://www.example.com/forgot-password/%s', $passwordToken->getToken()), 'expires_at' => $passwordToken->getExpiresAt(), ])); $this->mailer->send($message); } } ``` -------------------------------- ### Password Reset API Endpoints Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/index.md This section details the API endpoints provided by the CoopTilleulsForgotPasswordBundle for initiating and managing password resets. ```APIDOC ## POST /forgot-password/ ### Description Initiates the password reset process by receiving a user's email address. ### Method POST ### Endpoint /forgot-password/ ### Parameters #### Request Body - **email** (string) - Required - The email address of the user requesting a password reset. ### Request Example ```json { "email": "user@example.com" } ``` ### Response #### Success Response (200) - **message** (string) - Indicates the password reset request was successful. #### Response Example ```json { "message": "Password reset email sent successfully." } ``` ``` ```APIDOC ## GET /forgot-password/{tokenValue} ### Description Validates a password reset token. This endpoint is typically used to verify the token's authenticity before allowing a password update. ### Method GET ### Endpoint /forgot-password/{tokenValue} ### Parameters #### Path Parameters - **tokenValue** (string) - Required - The unique token generated for the password reset request. ### Response #### Success Response (200) - **token** (object) - Contains details about the valid token. - **isValid** (boolean) - True if the token is valid. - **expiresAt** (string) - The expiration date and time of the token. #### Response Example ```json { "token": { "isValid": true, "expiresAt": "2023-10-27T10:00:00+00:00" } } ``` ``` ```APIDOC ## POST /forgot-password/{tokenValue} ### Description Updates the user's password using a valid password reset token. ### Method POST ### Endpoint /forgot-password/{tokenValue} ### Parameters #### Path Parameters - **tokenValue** (string) - Required - The unique token generated for the password reset request. #### Request Body - **password** (string) - Required - The new password for the user. ### Request Example ```json { "password": "newSecurePassword123" } ``` ### Response #### Success Response (200) - **message** (string) - Indicates the password has been successfully updated. #### Response Example ```json { "message": "Password updated successfully." } ``` ``` -------------------------------- ### Request Password Reset Token Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Initiates the password reset process by requesting a token. The email address of the user is provided in the request body. For multiple providers, the 'FP-provider' header can be used to specify the provider. ```APIDOC ## POST /forgot-password/ ### Description Requests a password reset token for a given email address. ### Method POST ### Endpoint /forgot-password/ ### Parameters #### Request Body - **email** (string) - Required - The email address of the user requesting a password reset. - **username** (string) - Required - The username or email of the user requesting a password reset (used with FP-provider header). #### Headers - **Content-Type** (string) - Required - application/json - **FP-provider** (string) - Optional - Specifies the provider when multiple providers are configured. ### Request Example ```json { "email": "user@example.com" } ``` ### Response #### Success Response (204) No Content. Indicates the request was successful and a reset email has been sent (or will be sent). #### Error Response - 400 Bad Request: If the email or username is missing or invalid. - 404 Not Found: If the user associated with the email/username is not found. ``` -------------------------------- ### CreateTokenEvent: Send Password Reset Email Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Handles the `CreateTokenEvent` to send a password reset email to the user after a token has been generated. ```APIDOC ## Event: CreateTokenEvent ### Description This event is dispatched after a password reset token is successfully created for a user. It is intended to be handled by subscribers who will send a password reset email. ### Event Name `CoopTilleuls\ForgotPasswordBundle\Event\CreateTokenEvent` ### Handler Logic Subscribers should implement the `onCreateToken` method to send an email containing a password reset link. The link should include the generated token and point to the password update endpoint. ### Example Implementation (PHP) ```php // src/EventSubscriber/ForgotPasswordEventSubscriber.php namespace App\EventSubscriber; use CoopTilleuls\ForgotPasswordBundle\Event\CreateTokenEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mime\Email; use Twig\Environment; final class ForgotPasswordEventSubscriber implements EventSubscriberInterface { public function __construct( private readonly MailerInterface $mailer, private readonly Environment $twig ) {} public static function getSubscribedEvents(): array { return [ CreateTokenEvent::class => 'onCreateToken', ]; } public function onCreateToken(CreateTokenEvent $event): void { $passwordToken = $event->getPasswordToken(); $user = $passwordToken->getUser(); $message = (new Email()) ->from('no-reply@example.com') ->to($user->getEmail()) ->subject('Reset your password') ->html($this->twig->render( 'emails/reset_password.html.twig', [ 'reset_password_url' => sprintf( 'https://www.example.com/forgot-password/%s', $passwordToken->getToken() ), 'expires_at' => $passwordToken->getExpiresAt(), ] )); $this->mailer->send($message); } } ``` ``` -------------------------------- ### POST /forgot-password/{tokenValue} Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/index.md Resets the user's password using a provided token. Dispatches the `coop_tilleuls_forgot_password.update_password` event. ```APIDOC ## POST /forgot-password/{tokenValue} ### Description Resets the user's password using a provided token. This endpoint dispatches the `coop_tilleuls_forgot_password.update_password` event. ### Method POST ### Endpoint /forgot-password/{tokenValue} ### Parameters #### Path Parameters - **tokenValue** (string) - Required - The password reset token. #### Query Parameters - **FP-provider** (string) - Optional - Specifies the provider to use for password management. #### Request Body - **replace_with_your_password_field** (string) - Required - The new password for the user. ### Request Example ```json { "replace_with_your_password_field": "NewPassword" } ``` ### Response #### Success Response (200) - **message** (string) - Confirmation message that the password has been successfully reset. #### Response Example ```json { "message": "Password updated successfully." } ``` ``` -------------------------------- ### Handle UpdatePasswordEvent for Persistence Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Subscribes to the UpdatePasswordEvent to hash and save the new password to the database. Uses Doctrine and UserPasswordHasher. ```php namespace App\EventSubscriber; use CoopTilleuls\ForgotPasswordBundle\Event\UpdatePasswordEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; use Doctrine\ORM\EntityManagerInterface; final class ForgotPasswordEventSubscriber implements EventSubscriberInterface { public function __construct( private readonly UserPasswordHasherInterface $passwordHasher, private readonly EntityManagerInterface $entityManager ) {} public static function getSubscribedEvents(): array { return [UpdatePasswordEvent::class => 'onUpdatePassword']; } public function onUpdatePassword(UpdatePasswordEvent $event): void { $passwordToken = $event->getPasswordToken(); $user = $passwordToken->getUser(); $hashedPassword = $this->passwordHasher->hashPassword($user, $event->getPassword()); $user->setPassword($hashedPassword); $this->entityManager->persist($user); $this->entityManager->flush(); } } ``` -------------------------------- ### Force Push to Update Pull Request Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/CONTRIBUTING.md Force push your changes to update an existing pull request. Use this command after squashing commits or making other history-altering changes. ```bash git push --force ``` -------------------------------- ### Send Custom Email on Password Reset Request (PHP) Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/usage.md This subscriber listens for the `CreateTokenEvent` to send a custom password reset email to the user. It utilizes Symfony's Mailer and Twig for rendering the email content. The event is dispatched when a user requests a password reset via the API. ```php namespace App\EventSubscriber; // ... use CoopTilleuls\ForgotPasswordBundle\Event\CreateTokenEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mime\Email; use Twig\Environment; final class ForgotPasswordEventSubscriber implements EventSubscriberInterface { public function __construct(private readonly MailerInterface $mailer, private readonly Environment $twig) { } public static function getSubscribedEvents(): array { return [ // Symfony 4.3 and inferior, use 'coop_tilleuls_forgot_password.create_token' event name CreateTokenEvent::class => 'onCreateToken', ]; } public function onCreateToken(CreateTokenEvent $event): void { $passwordToken = $event->getPasswordToken(); $user = $passwordToken->getUser(); $message = (new Email()) ->from('no-reply@example.com') ->to($user->getEmail()) ->subject('Reset your password') ->html($this->twig->render( 'ResetPassword/mail.html.twig', [ 'reset_password_url' => sprintf('https://www.example.com/forgot-password/%s', $passwordToken->getToken()), ] )); $this->mailer->send($message); } } ``` -------------------------------- ### Update Password Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Allows a user to set a new password using a valid reset token. This action triggers an event to handle password encoding and persistence. ```APIDOC ## POST /forgot-password/{tokenValue} ### Description Submits a new password using a valid reset token. ### Method POST ### Endpoint /forgot-password/{tokenValue} ### Parameters #### Path Parameters - **tokenValue** (string) - Required - The password reset token. #### Request Body - **password** (string) - Required - The new password for the user. ### Request Example ```json { "password": "NewSecureP4$$w0rd" } ``` ### Response #### Success Response (204) No Content. Indicates the password has been successfully updated. #### Error Response - 404 Not Found: If the token is invalid or has expired. - 400 Bad Request: If the 'password' field is missing in the request body. ``` -------------------------------- ### Implement ForgotPasswordEventSubscriber for Symfony Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt This subscriber listens for CreateTokenEvent, UpdatePasswordEvent, and UserNotFoundEvent. It handles sending reset emails via MailerInterface, hashing passwords with UserPasswordHasherInterface, and logging security events. ```php namespace App\EventSubscriber; use CoopTilleuls\ForgotPasswordBundle\Event\CreateTokenEvent; use CoopTilleuls\ForgotPasswordBundle\Event\UpdatePasswordEvent; use CoopTilleuls\ForgotPasswordBundle\Event\UserNotFoundEvent; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mime\Email; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; use Twig\Environment; final class ForgotPasswordEventSubscriber implements EventSubscriberInterface { public function __construct( private readonly MailerInterface $mailer, private readonly Environment $twig, private readonly UserPasswordHasherInterface $passwordHasher, private readonly EntityManagerInterface $entityManager, private readonly LoggerInterface $logger ) { } public static function getSubscribedEvents(): array { return [ CreateTokenEvent::class => 'onCreateToken', UpdatePasswordEvent::class => 'onUpdatePassword', UserNotFoundEvent::class => 'onUserNotFound', ]; } public function onCreateToken(CreateTokenEvent $event): void { $passwordToken = $event->getPasswordToken(); $user = $passwordToken->getUser(); $email = (new Email()) ->from('security@example.com') ->to($user->getEmail()) ->subject('Password Reset Request') ->html($this->twig->render('emails/reset_password.html.twig', [ 'user' => $user, 'token' => $passwordToken->getToken(), 'expires_at' => $passwordToken->getExpiresAt(), ])); $this->mailer->send($email); $this->logger->info('Password reset email sent', [ 'user_id' => $user->getId(), 'email' => $user->getEmail(), ]); } public function onUpdatePassword(UpdatePasswordEvent $event): void { $passwordToken = $event->getPasswordToken(); $user = $passwordToken->getUser(); $hashedPassword = $this->passwordHasher->hashPassword( $user, $event->getPassword() ); $user->setPassword($hashedPassword); $this->entityManager->persist($user); $this->entityManager->flush(); $this->logger->info('Password successfully reset', [ 'user_id' => $user->getId(), ]); } public function onUserNotFound(UserNotFoundEvent $event): void { $this->logger->warning('Password reset attempted for unknown user', [ 'context' => $event->getContext(), ]); } } ``` -------------------------------- ### AbstractPasswordToken Methods Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt The AbstractPasswordToken class provides core token functionality. Extend this class for your PasswordToken entity to leverage methods like getToken, setToken, getExpiresAt, setExpiresAt, and isExpired. ```php use CoopTilleuls\ForgotPasswordBundle\Entity\AbstractPasswordToken; // AbstractPasswordToken provides these methods: $token->getToken(); // Returns the token string $token->setToken(string $token); // Sets the token string $token->getExpiresAt(); // Returns DateTime of expiration $token->setExpiresAt(\DateTime $date); // Sets expiration date $token->isExpired(); // Returns true if token has expired // Your implementation must define: abstract public function getId(); // Return the entity ID abstract public function getUser(); // Return the associated user abstract public function setUser($user); // Set the associated user ``` -------------------------------- ### Interactive Git Rebase for Squashing Commits Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/CONTRIBUTING.md Use interactive rebase to squash multiple Git commits into a single commit. This is useful for cleaning up your commit history before submitting a pull request. ```bash git rebase -i HEAD~3 ``` -------------------------------- ### POST /forgot-password/{tokenValue} Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/usage.md Finalizes the password reset process by submitting a new password associated with a valid token. ```APIDOC ## POST /forgot-password/{tokenValue} ### Description Updates the user's password using the provided token. Dispatches the `coop_tilleuls_forgot_password.update_password` event. ### Method POST ### Endpoint /forgot-password/{tokenValue} ### Parameters #### Path Parameters - **tokenValue** (string) - Required - The unique token sent to the user via email. #### Request Body - **password** (string) - Required - The new password to be set for the user. ### Request Example { "password": "P4$$w0rd" } ### Response #### Success Response (200) - **status** (string) - Confirmation that the password has been updated successfully. ``` -------------------------------- ### Validate User Password Strength Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/docs/usage.md Applies the PasswordStrength constraint to a User entity property to ensure new passwords meet security requirements. ```php namespace App\Entity; use Symfony\Component\Validator\Constraints as Assert; class User { #[Assert\PasswordStrength] protected $rawPassword; } ``` -------------------------------- ### UpdatePasswordEvent: Save New Password Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Handles the `UpdatePasswordEvent` to encode and persist the user's new password after it has been submitted via the reset token. ```APIDOC ## Event: UpdatePasswordEvent ### Description This event is dispatched when a user submits a new password using a valid reset token. It is intended for subscribers to handle the encoding and persistence of the new password. ### Event Name `CoopTilleuls\ForgotPasswordBundle\Event\UpdatePasswordEvent` ### Handler Logic Subscribers should implement the `onUpdatePassword` method to hash the provided password using a secure password hashing mechanism and then update the user's password in the data store. ### Example Implementation (PHP) ```php // src/EventSubscriber/ForgotPasswordEventSubscriber.php namespace App\EventSubscriber; use CoopTilleuls\ForgotPasswordBundle\Event\UpdatePasswordEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; use Doctrine\ORM\EntityManagerInterface; final class ForgotPasswordEventSubscriber implements EventSubscriberInterface { public function __construct( private readonly UserPasswordHasherInterface $passwordHasher, private readonly EntityManagerInterface $entityManager ) {} public static function getSubscribedEvents(): array { return [ UpdatePasswordEvent::class => 'onUpdatePassword', ]; } public function onUpdatePassword(UpdatePasswordEvent $event): void { $passwordToken = $event->getPasswordToken(); $user = $passwordToken->getUser(); // Hash the new password $hashedPassword = $this->passwordHasher->hashPassword( $user, $event->getPassword() ); $user->setPassword($hashedPassword); $this->entityManager->persist($user); $this->entityManager->flush(); } } ``` ``` -------------------------------- ### Update Password via REST API Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Submits a new password to the endpoint using a valid token. Requires a POST request with the new password in the body. ```bash curl -X POST https://api.example.com/forgot-password/abc123def456 \ -H "Content-Type: application/json" \ -d '{"password": "NewSecureP4$$w0rd"}' ``` -------------------------------- ### Validate Token Source: https://context7.com/cooptilleuls/cooptilleulsforgotpasswordbundle/llms.txt Validates a password reset token and retrieves token information, including expiration time and associated user data. ```APIDOC ## GET /forgot-password/{tokenValue} ### Description Validates a password reset token and returns its associated data. ### Method GET ### Endpoint /forgot-password/{tokenValue} ### Parameters #### Path Parameters - **tokenValue** (string) - Required - The password reset token to validate. #### Headers - **Accept** (string) - Required - application/json ### Response #### Success Response (200) - **token** (string) - The validated password reset token. - **expiresAt** (string) - The expiration date and time of the token in ISO 8601 format. - **user** (object) - The user object associated with the token (details depend on serialization groups). #### Response Example ```json { "token": "abc123def456", "expiresAt": "2024-01-16T12:00:00+00:00", "user": { ... } } ``` #### Error Response - 404 Not Found: If the token is invalid or has expired. ``` -------------------------------- ### Fix Code Style Issues with PHP CS Fixer Source: https://github.com/cooptilleuls/cooptilleulsforgotpasswordbundle/blob/2.0/CONTRIBUTING.md Automatically fix code style issues in the project using the PHP CS Fixer tool. This command applies Symfony coding standards to your files. ```bash vendor/bin/php-cs-fixer fix ```