Description
Security - tagging UserCheckerInterface
When working with custom authentications, you most likely want to implement a custom UserChecker. The standard "form" supports this but doesn't really allow customization. "simple_form" does not have a standard implementation for this, sadly.
Proposal
I came to the conclusion that it might be a lot easier to add some custom account/user validation. I was creating my own Authentication and user provider and stumbled against the choice to implement the Simple variant or the normal (older) variant.
One of the main reasons I couldn't implement the existing variant, was due to the lack of customization of the UserChecker. Before loading the user, I had to know if the user was supposed to see a captcha or if the IP of the user was blocked. Before the authentication, I wanted to know if the account had any blocks. After the authentication, I wanted to load a secondary Entity that I would check some additional account statuses. I could not work around this with existing code so had to write a custom provider for this.
A simple solution would be to allow the firewall options for a certain authentication method to allow passing the ID of a custom service. This would mean you'd get the following option:
security:
firewall:
secured_area:
(simple_)form:
user_checker_service: my.user_checker
As long as your service would implement the UserChecker Interface, this would all be possible. However, this isn't really flexible... You could do the following:
security:
firewall:
secured_area:
(simple_)form:
user_checkers:
- my.user_checker.ip_block
- my.user_checker.captcha_block
- my.user_checker.banned_account
- my.user_checker.accepts_latest_eula
- vendor.bundle.extra_user_check
With this solution, you would be able to add extra checks on demand. It would be a really nice addition to work with the Simple provider as well, but that would introduce BC breaks, but solve a lot of nasty code.
class MyBannedAccountUserChecker implements
PreUserLoadCheckerInterface,
PreAuthUserCheckerInterface,
PostAuthUserCheckerInterface
{
public function checkPreUserLoad()
{
// additional checks BEFORE the user is being load...
// good example is to check IP block, captcha's, other requirements
}
public function checkPreAuth(UserInterface $user)
{
if (!$user instanceof MyUser) {
return;
}
// .. get the entity where ever you store if the user is banned
if ($banned) {
throw new AccountBannedException($user); // extends AuthenticationException
}
}
public function checkPostAuth(UserInterface $user) { ... }
}
I think this example should demonstrate exactly what I mean. This will give you flexibility while it will also make it easier to configure the security component user checks. I have noticed examples of people who are totally lost in this component and have no clue where to add the custom checks for this. As far as I know it's only possible to modify the current existing UserChecker by a CompilerPass. It's tightly coupled with the "form" login method and the only available implementation works only with the AdvancedUserInterface (which imo should be rm -rf'd).
To prevent backwards incompatibility issues, the current UserCheckerInterface should not be removed, although the splitting of pre and post auth checks can cause a minor inconvenience.