Skip to content

Instantly share code, notes, and snippets.

@matthbull
Created July 12, 2017 09:17
Show Gist options
  • Save matthbull/8ab0a3b8e3ada525ecae3c733668afee to your computer and use it in GitHub Desktop.
Save matthbull/8ab0a3b8e3ada525ecae3c733668afee to your computer and use it in GitHub Desktop.
JWTGuard authenticator - symfony 3
<?php
/**
* Date: 07/04/17
* Time: 09:51
*/
namespace AppBundle\Security;
use AppBundle\Api\ResponseFactory;
use AppBundle\Exception\ApiProblem;
use AppBundle\Services\LoggerAwareInterface;
use AppBundle\Traits\LoggerAwareTrait;
use Doctrine\ORM\EntityManager;
use Lexik\Bundle\JWTAuthenticationBundle\Encoder\JWTEncoderInterface;
use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\AuthorizationHeaderTokenExtractor;
use Monolog\Logger;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
/**
*
* Class JwtTokenAuthenticator
*
* This is a guard class, specified in security.yml. It only looks for tokens and then decodes it to get the user.
* JWT tokens use the jwt private and public keys in /var/jwt to ensure that the token hasnt been tampered with.
* So whilst its easy to decode them, a hacker cant change them. This provides an adequate security layer.
*
* @file security.yml
*
*
* @package AppBundle\Security
* @author: Matt Holbrook-Bull <matt@ampisoft.com>
*/
class JwtTokenAuthenticator extends AbstractGuardAuthenticator implements LoggerAwareInterface
{
use LoggerAwareTrait;
/**
* @var JWTEncoderInterface
*/
private $encoder;
/**
* @var EntityManager
*/
private $em;
/**
* @var ResponseFactory
*/
private $responseFactory;
/**
* JwtTokenAuthenticator constructor.
*
* @param JWTEncoderInterface $encoder
* @param EntityManager $em
* @param ResponseFactory $responseFactory
*/
public function __construct(JWTEncoderInterface $encoder, EntityManager $em, ResponseFactory $responseFactory)
{
$this->encoder = $encoder;
$this->em = $em;
$this->responseFactory = $responseFactory;
}
/**
* @param Request $request
* @return array|bool|false|null|string
*/
public function getCredentials(Request $request)
{
// get the token information and return it
$extractor = new AuthorizationHeaderTokenExtractor(
'Bearer',
'Authorization');
$token = $extractor->extract($request);
if(!$token) {
// only log for closed endpoints
if(strpos($request->getUri(), '/login/app-login.php') === false) {
$this->logger->log(Logger::DEBUG,
sprintf('[API FIREWALL] No token detected trying to access: %s from IP: %s',$request->getUri(), $request->getClientIp()));
}
return null;
}
return $token;
}
/**
* @param mixed $credentials
* @param UserProviderInterface $userProvider
* @return \AppBundle\Entity\Player|null|object
*/
public function getUser($credentials, UserProviderInterface $userProvider)
{
$creds = $this->encoder->decode($credentials);
if(!$creds) {
throw new CustomUserMessageAuthenticationException('Invalid token');
}
return $this->em->getRepository('AppBundle:Player')
->findOneBy(['username' => $creds['username']]);
}
/**
* @param mixed $credentials
* @param UserInterface $user
* @return bool
*/
public function checkCredentials($credentials, UserInterface $user)
{
return true;
}
/**
* @param Request $request
* @param AuthenticationException $exception
* @return null|Response|void
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
}
/**
* @param Request $request
* @param TokenInterface $token
* @param string $providerKey
* @return null|Response|void
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
// do nothing
}
/**
* @return bool
*/
public function supportsRememberMe()
{
return false;
}
/**
* Entry point is only used if all else fails.
*
* @param Request $request
* @param AuthenticationException|null $authException
* @return \Symfony\Component\HttpFoundation\JsonResponse
*/
public function start(Request $request, AuthenticationException $authException = null)
{
// return a failed response type
$apiProblem = new ApiProblem(401);
$message = $authException ? $authException->getMessageKey() : 'Missing credentials';
$apiProblem->set('detail', $message);
$this->logger->error($apiProblem->get('detail'));
return $this->responseFactory->createResponse($apiProblem);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment