I'm trying to redirect the user to force them to change/reset their password when it is their first login and when they have the property "IsPasswordChange" false but the issue is that I have a too many redirects error. If someone can help me find the problem, thank you.
LoginEvent.php :
class LoginEvent implements EventSubscriberInterface
{
private Security $security;
private UrlGeneratorInterface $urlGenerator;
public function __construct(Security $security, UrlGeneratorInterface $urlGenerator)
{
$this->security = $security;
$this->urlGenerator = $urlGenerator;
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => [
['forcePasswordChange', 0]
],
];
}
public function forcePasswordChange(RequestEvent $event): void
{
// only deal with the main request, disregard subrequests
if (!$event->isMainRequest()) {
return;
}
// if we are visiting the password change route, no need to redirect
if ($event->getRequest()->attributes->get('_route') == 'app_reset_password') {
return;
}
$user = $this->security->getUser();
// if you do not have a valid user, it means it's not an authenticated request, so it's not our concern
if (!$user instanceof Partenaire) {
return;
}
// if it's not their first login, and have is password changed = true, no need to redirect
if ($user->isIsPasswordChange()) {
return;
}
// if we get here, it means we need to redirect them to the password change view.
$event->setResponse(new RedirectResponse($this->urlGenerator->generate('app_reset_password', [
'token' => $user->getUserIdentifier()
])));
}
}
security.yaml :
security:
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
chain_provider:
chain:
providers: [ app_admin_provider, app_partenaire_provider, app_structure_provider ]
# used to reload user from session & other features (e.g. switch_user)
app_admin_provider:
entity:
class: App\Entity\Admin
property: email
app_partenaire_provider:
entity:
class: App\Entity\Partenaire
property: email
app_structure_provider:
entity:
class: App\Entity\Structure
property: email
# used to reload user from session & other features (e.g. switch_user)
# used to reload user from session & other features (e.g. switch_user)
# used to reload user from session & other features (e.g. switch_user)
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: chain_provider
custom_authenticator: App\Security\LoginFormAuthenticator
logout:
path: app_logout
# where to redirect after logout
# target: app_any_route
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#the-firewall
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/homepage, roles: [ROLE_ADMIN, ROLE_PARTENAIRE] },
- { path: ^/homepage-admin, roles: ROLE_ADMIN },
role_hierarchy:
ROLE_ADMIN: [ ROLE_USER, ROLE_PARTENAIRE, ROLE_STRUCTURE ]
when@test:
security:
password_hashers:
# By default, password hashers are resource intensive and take time. This is
# important to generate secure password hashes. In tests however, secure hashes
# are not important, waste resources and increase test times. The following
# reduces the work factor to the lowest possible values.
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
algorithm: auto
cost: 4 # Lowest possible value for bcrypt
time_cost: 3 # Lowest possible value for argon
memory_cost: 10 # Lowest possible value for argon
My Answer:
"forcePasswordChange" set to true in Symfony.
Here's how you can achieve this:
1. In your security configuration file (usually located at config/packages/security.yaml), add a new entry under your firewall configuration to check if the user needs to reset their password:
yaml
security:
firewalls:
main:
pattern: ^/
form_login:
login_path: app_login
check_path: app_login
default_target_path: app_home
logout:
path: app_logout
target: app_login
# Add the following entry to check if the user needs to reset their password
entry_point: App\Security\ForcePasswordChangeAuthenticator
2. Create a new class called ForcePasswordChangeAuthenticator that implements the AuthenticationEntryPointInterface interface. This class will handle the redirection logic:
php
// src/Security/ForcePasswordChangeAuthenticator.php
namespace App\Security;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class ForcePasswordChangeAuthenticator implements AuthenticationEntryPointInterface
{
public function start(Request $request, AuthenticationException $authException = null)
{
// Check if the user needs to reset their password
if ($request->getUser()->getForcePasswordChange()) {
return new Response('Redirect to password reset page');
}
return new Response('Redirect to homepage');
}
}
3. Update your User entity to include the property "forcePasswordChange" and make sure it is set to true for users who need to reset their password:
php
// src/Entity/User.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class User
{
// Add the property "forcePasswordChange"
/**
* @ORM\Column(type="boolean")
*/
private $forcePasswordChange;
public function getForcePasswordChange(): ?bool
{
return $this->forcePasswordChange;
}
public function setForcePasswordChange(bool $forcePasswordChange): self
{
$this->forcePasswordChange = $forcePasswordChange;
return $this;
}
}
With these changes in place, the user will be redirected to the password reset page if they need to reset their password on their first login or if the "forcePasswordChange" property is set to true. Otherwise, they will be redirected to the homepage.
Rate this post
4 of 5 based on 9068 votesComments