-
Notifications
You must be signed in to change notification settings - Fork 114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to listen from grant events in Symfony #257
Comments
+1 Did you somehow solve your question? |
I created this class: use League\Event\Emitter;
use League\Event\EmitterInterface;
use League\Event\EventInterface;
use League\Event\ListenerInterface;
use League\OAuth2\Server\RequestEvent;
use Psr\Log\LoggerInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class LeagueEmitterDispatcherRelay implements ListenerInterface
{
private EmitterInterface $leagueEmitter;
private EventDispatcherInterface $symfonyDispatcher;
// private LoggerInterface $logger;
public function __construct(EventDispatcherInterface $symfonyDispatcher/*, LoggerInterface $logger */)
{
$this->symfonyDispatcher = $symfonyDispatcher;
// $this->logger = $logger;
}
public function getLeagueEmitterRelay(): EmitterInterface
{
if (!isset($this->leagueEmitter)) {
$this->leagueEmitter = $this->buildEmitterRelay();
}
return $this->leagueEmitter;
}
public function buildEmitterRelay(): EmitterInterface
{
$emitter = new Emitter();
// unused for now
// $emitter->addListener(RequestEvent::ACCESS_TOKEN_ISSUED, $this);
$emitter->addListener(RequestEvent::REFRESH_TOKEN_ISSUED, $this);
return $emitter;
}
/**
* {@inheritdoc}
* Handle an event.
*/
public function handle(EventInterface $event): void
{
// $this->logger->debug('relay league oauth event: '.$event->getName());
$this->symfonyDispatcher->dispatch($event, $event->getName());
}
/**
* {@inheritdoc}
*/
public function isListener($listener): bool
{
return $listener === $this;
}
} And wired it in # Relay to dispatch league oauth events in symfony, will make the link
# between the two event systems, used as a factory to get the league
# emitter.
App\Auth\LeagueEmitterDispatcherRelay:
# The league emitter service itself, used in the container compiler pass to
# provide it to the PasswordGrant class.
League\Event\EmitterInterface:
factory: ['@App\Auth\LeagueEmitterDispatcherRelay', 'getLeagueEmitterRelay'] The instance of that class should be injected with the league emitter, listen to events defined in But it's been a while since I've done this, and we do not use it anymore, so it may not work out of the box. |
In general, for some reason I could not use the code that you suggested, so I just redefined the class with some changes 🤷♀️ <?php
declare(strict_types=1);
namespace App\Security\Grant;
use App\Event\AccessTokenIssuedEvent;
use App\Event\RefreshTokenIssuedEvent;
use DateInterval;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\UserEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\AbstractGrant;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
use League\OAuth2\Server\RequestEvent;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use Psr\Http\Message\ServerRequestInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class PasswordGrant extends AbstractGrant
{
private EventDispatcherInterface $dispatcher;
public function __construct(
UserRepositoryInterface $userRepository,
RefreshTokenRepositoryInterface $refreshTokenRepository,
EventDispatcherInterface $dispatcher
) {
$this->setUserRepository($userRepository);
$this->setRefreshTokenRepository($refreshTokenRepository);
$this->refreshTokenTTL = new DateInterval('P1M');
$this->dispatcher = $dispatcher;
}
/**
* {@inheritdoc}
*/
public function getIdentifier(): string
{
return 'password';
}
/**
* {@inheritdoc}
*/
public function respondToAccessTokenRequest(
ServerRequestInterface $request,
ResponseTypeInterface $responseType,
DateInterval $accessTokenTTL
): ResponseTypeInterface {
// Validate request
$client = $this->validateClient($request);
$scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope));
$user = $this->validateUser($request, $client);
// Finalize the requested scopes
$finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier());
// Issue and persist new access token
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $finalizedScopes);
//$this->getEmitter()->emit(new RequestAccessTokenEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request, $accessToken));
$this->dispatcher->dispatch(new AccessTokenIssuedEvent($accessToken, $request));
$responseType->setAccessToken($accessToken);
// Issue and persist new refresh token if given
$refreshToken = $this->issueRefreshToken($accessToken);
if ($refreshToken !== null) {
$this->dispatcher->dispatch(new RefreshTokenIssuedEvent($refreshToken, $request));
//$this->getEmitter()->emit(new RequestRefreshTokenEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request, $refreshToken));
$responseType->setRefreshToken($refreshToken);
}
return $responseType;
}
/**
* @throws OAuthServerException
*
* @return UserEntityInterface
*/
protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client)
{
$username = $this->getRequestParameter('username', $request);
if (!\is_string($username)) {
throw OAuthServerException::invalidRequest('username');
}
$password = $this->getRequestParameter('password', $request);
if (!\is_string($password)) {
throw OAuthServerException::invalidRequest('password');
}
$user = $this->userRepository->getUserEntityByUserCredentials(
$username,
$password,
$this->getIdentifier(),
$client
);
if ($user instanceof UserEntityInterface === false) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidCredentials();
}
return $user;
}
} In services.yaml: League\OAuth2\Server\Grant\PasswordGrant:
class: App\Security\Grant\PasswordGrant Events: AccessTokenIssued: <?php
declare(strict_types=1);
namespace App\Event;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\RequestEvent;
use Psr\Http\Message\ServerRequestInterface;
use Symfony\Contracts\EventDispatcher\Event;
class AccessTokenIssuedEvent extends Event
{
public const NAME = RequestEvent::ACCESS_TOKEN_ISSUED;
private AccessTokenEntityInterface $accessToken;
private ServerRequestInterface $request;
public function __construct(AccessTokenEntityInterface $accessToken, ServerRequestInterface $request)
{
$this->accessToken = $accessToken;
$this->request = $request;
}
public function getAccessToken(): AccessTokenEntityInterface
{
return $this->accessToken;
}
public function getRequest(): ServerRequestInterface
{
return $this->request;
}
} RefreshTokenIssued: <?php
declare(strict_types=1);
namespace App\Event;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
use League\OAuth2\Server\RequestEvent;
use Psr\Http\Message\ServerRequestInterface;
use Symfony\Contracts\EventDispatcher\Event;
class RefreshTokenIssuedEvent extends Event
{
public const NAME = RequestEvent::REFRESH_TOKEN_ISSUED;
private RefreshTokenEntityInterface $refreshToken;
private ServerRequestInterface $request;
public function __construct(RefreshTokenEntityInterface $refreshToken, ServerRequestInterface $request)
{
$this->refreshToken = $refreshToken;
$this->request = $request;
}
public function getRefreshToken(): RefreshTokenEntityInterface
{
return $this->refreshToken;
}
public function getRequest(): ServerRequestInterface
{
return $this->request;
}
} And in Subscriber: <?php
declare(strict_types=1);
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class SomeSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
AccessTokenIssuedEvent::class => 'onAccessTokenIssued',
RefreshTokenIssuedEvent::class => 'onRefreshTokenIssued',
];
}
// ....
} |
Hi,
I see this line of code in password grant:
The problem is that this bundle does not provide an emitter to the grant implementations of PHP-league (
getEmitter()
creates a new Emitter instance).How can I add symfony event listeners to listen for those events ?
I guess I would have to create an emitter service in services.yaml but I do not know how I could inject it into the league library through your bundle.
Thank you
The text was updated successfully, but these errors were encountered: