Skip to content

Instantly share code, notes, and snippets.

@yceruto
Created November 1, 2020 17:21
Show Gist options
  • Save yceruto/f1500b350065481568a3b2971fde8d0f to your computer and use it in GitHub Desktop.
Save yceruto/f1500b350065481568a3b2971fde8d0f to your computer and use it in GitHub Desktop.
Guard Authenticator for OAuth2Bundle (https://github.com/trikoder/oauth2-bundle)
<?php
namespace App\Security\Authentication\Token;
use Symfony\Component\Security\Guard\Token\GuardTokenInterface;
use Trikoder\Bundle\OAuth2Bundle\Security\Authentication\Token\OAuth2Token;
class OAuth2GuardToken implements GuardTokenInterface
{
private $token;
public function __construct(OAuth2Token $token)
{
$this->token = $token;
}
// ...
}
<?php
namespace App\Security\Guard;
use App\Security\Authentication\Token\OAuth2GuardToken;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\ResourceServer;
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Trikoder\Bundle\OAuth2Bundle\Security\Authentication\Token\OAuth2Token;
use Trikoder\Bundle\OAuth2Bundle\Security\Authentication\Token\OAuth2TokenFactory;
use Trikoder\Bundle\OAuth2Bundle\Security\Exception\InsufficientScopesException;
use Trikoder\Bundle\OAuth2Bundle\Security\Exception\Oauth2AuthenticationFailedException;
class OAuth2TokenAuthenticator extends AbstractGuardAuthenticator
{
private $httpMessageFactory;
private $resourceServer;
private $oauth2TokenFactory;
private $psr7Request;
public function __construct(HttpMessageFactoryInterface $httpMessageFactory, ResourceServer $resourceServer, OAuth2TokenFactory $oauth2TokenFactory)
{
$this->httpMessageFactory = $httpMessageFactory;
$this->resourceServer = $resourceServer;
$this->oauth2TokenFactory = $oauth2TokenFactory;
}
public function start(Request $request, AuthenticationException $authException = null): Response
{
$exception = new UnauthorizedHttpException('Bearer');
return new JsonResponse(['code' => $exception->getCode(), 'message' => ''], $exception->getCode(), $exception->getHeaders());
}
public function supports(Request $request): bool
{
return 0 === strpos($request->headers->get('Authorization'), 'Bearer ');
}
public function getCredentials(Request $request)
{
$psr7Request = $this->httpMessageFactory->createRequest($request);
try {
$this->psr7Request = $this->resourceServer->validateAuthenticatedRequest($psr7Request);
} catch (OAuthServerException $e) {
throw new AuthenticationException('The resource server rejected the request.', 0, $e);
}
return $this->psr7Request->getAttribute('oauth_user_id');
}
public function getUser($userIdentifier, UserProviderInterface $userProvider): ?UserInterface
{
if ('' === $userIdentifier) {
/*
* If the identifier is an empty string, that means that the
* access token isn't bound to a user defined in the system.
*/
return null;
}
return $userProvider->loadUserByUsername($userIdentifier);
}
public function checkCredentials($token, UserInterface $user): bool
{
return true;
}
public function createAuthenticatedToken(UserInterface $user, $providerKey): OAuth2GuardToken
{
$oauth2Token = $this->oauth2TokenFactory->createOAuth2Token($this->psr7Request, $user, $providerKey);
if (!$this->isAccessToRouteGranted($oauth2Token)) {
throw InsufficientScopesException::create($oauth2Token);
}
$oauth2Token->setAuthenticated(true);
return new OAuth2GuardToken($oauth2Token);
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
if ($exception instanceof InsufficientScopesException || $exception instanceof Oauth2AuthenticationFailedException) {
return new JsonResponse([
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
], $exception->getCode());
}
$previous = $exception->getPrevious();
if ($previous instanceof OAuthServerException) {
return new JsonResponse([
'code' => $previous->getHttpStatusCode(),
'message' => $exception->getMessage(),
], $previous->getHttpStatusCode(), $previous->getHttpHeaders());
}
return $this->start($request, $exception);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey): ?Response
{
return $this->psr7Request = null;
}
public function supportsRememberMe(): bool
{
return false;
}
private function isAccessToRouteGranted(OAuth2Token $token): bool
{
$routeScopes = $this->psr7Request->getAttribute('oauth2_scopes', []);
if ([] === $routeScopes) {
return true;
}
$tokenScopes = $token
->getAttribute('server_request')
->getAttribute('oauth_scopes');
/*
* If the end result is empty that means that all route
* scopes are available inside the issued token scopes.
*/
return [] === array_diff($routeScopes, $tokenScopes);
}
}
security:
# ...
firewalls:
api:
# ...
guard:
entry_point: App\Security\Guard\OAuth2TokenAuthenticator
authenticators:
- App\Security\Guard\OAuth2TokenAuthenticator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment