The password grant issues access and refresh tokens bound to both a client and a user. Because user authentication logic varies between applications, the bundle provides the league.oauth2_server.event.user_resolve event.
To implement custom user resolution, you must create an event listener that listens for this event. Inside the listener, you are responsible for:
- Retrieving the user from your user provider using the username provided in the event.
- Verifying that the user implements
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface. - Validating the provided password against the user's stored hash.
- Calling
$event->setUser($user) to bind the authenticated user to the token issuance process.
If the credentials are invalid or the user cannot be found, simply return from the listener without calling setUser().
<?php
namespace App\EventListener;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use League\Bundle\OAuth2ServerBundle\Event\UserResolveEvent;
final class UserResolveListener
{
private $userProvider;
private $userPasswordHasher;
public function __construct(UserProviderInterface $userProvider, UserPasswordHasherInterface $userPasswordHasher)
{
$this->userProvider = $userProvider;
$this->userPasswordHasher = $userPasswordHasher;
}
public function onUserResolve(UserResolveEvent $event): void
{
try {
$user = $this->userProvider->loadUserByIdentifier($event->getUsername());
} catch (AuthenticationException $e) {
return;
}
if (null === $user || !($user instanceof PasswordAuthenticatedUserInterface)) {
return;
}
if (!$this->userPasswordHasher->isPasswordValid($user, $event->getPassword())) {
return;
}
$event->setUser($user);
}
}