Infinite redirection loop problem
Bundle version: 7.0.0 Symfony version: 7.0.1 PHP version: 8.2.10
Description
Throwing an exception in user_checker checkPostAuth() ends in infinite redirection loop.
To Reproduce
- Configure bundle using email tokens.
- Add user checker in
security.yaml
security:
firewalls:
website:
lazy: true
provider: app_user_provider
user_checker: App\Security\UserChecker
...
-
App\Security\UserChecker
<?php
namespace App\Security;
use Symfony\Component\Security\Core\Exception\CredentialsExpiredException;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class UserChecker implements UserCheckerInterface
{
public function checkPreAuth(UserInterface $user): void
{
}
public function checkPostAuth(UserInterface $user): void
{
throw new CredentialsExpiredException();
}
}
- Now login as usual, enter the code from the mail, submit form and you will get the infinite redirection loop.
I can reproduce this behavior, but I don't yet understand where this is originating from.
My recommendation would be to move your check to the checkPreAuth phase, so it's already evaluated on the initial login. Due to 2fa, the checkPostAuth is delayed until the 2fa process has been complete and that's where it's causing the issues.
Checker's checkPostAuth check is executed on the AuthenticationSuccessEvent, which happens once the TwoFactorToken was unwrapped. The original authenticated token is about to become the new security token, though by that point it's not written back into the token storage yet.
When an exception happens on the AuthenticationSuccessEvent, the TwoFactorToken is never replaced in the token storage and stays there forever.
The exception causes the DefaultAuthenticationFailureHandler to trigger, redirects back to the 2fa form to display the error.
2fa form controller receives a TwoFactorToken without any 2fa providers available (all of them have been completed), so it responds with an AccessDeniedException. The AccessDeniedException in combination with a TwoFactorToken redirects back to the 2fa form. Repeat.
No idea how this can be properly handled. Most reasonable approach imo would be to move checks to checkPreAuth, to ensure the user is validated before 2fa even kicks in.