Skip to content

Instantly share code, notes, and snippets.

@settermjd
Last active August 24, 2021 12:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save settermjd/268d4eedb6b13bf9d70feb192e37823b to your computer and use it in GitHub Desktop.
Save settermjd/268d4eedb6b13bf9d70feb192e37823b to your computer and use it in GitHub Desktop.
Create and validate a JWT in Mezzio (PHP)
<?php
// File location: config/autoload/auth.global.php
return [
'auth' => [
'jwt' => [
// A secret key that you've generated in some secure/random fashion.
'key' => 'XicRF1ZLIUc+NzB4uqdaVlyVNSucfR90kmoAiuOGRi0=',
'claim' => [
'iss' => 'https:localdomain.dev',
'aud' => 'https:localdomain.dev',
],
// A DateTime interval dictating how long the token should be valid for
'expiryPeriod' => 'P10D',
'leeway' => 60,
'allowedAlgorithms' => [
'HS256'
]
]
],
];
<?php
// File location: src/App/src/Middleware/Authorization/JWTMiddleware.php
declare(strict_types=1);
namespace App\Middleware\Authorization;
use DateTime;
use DateInterval;
use \Firebase\JWT\JWT;
use Laminas\Diactoros\Response\RedirectResponse;
use Laminas\Diactoros\Response\TextResponse;
use Mezzio\Authentication\UserInterface;
use Mezzio\Session\SessionInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function sprintf;
/**
* Class JWTMiddleware
*
* Requires firebase/php-jwt and mezzio-session.
* I need to finish up a comprehensive tutorial for
* authenticating with sessions in Mezzio.
*
* @package App\Middleware\Authorization
*/
class JWTMiddleware implements MiddlewareInterface
{
private array $config;
/**
* JWTMiddleware constructor.
* @param array $config
*/
public function __construct(array $config)
{
$this->config = $config;
}
/**
* Generate a JWT
*
* This implementation uses a session attribute set using mezzio-session
* There are likely other criteria that you can use, if you require any.
*
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
* @throws \Exception
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
/** @var SessionInterface $session */
$session = $request->getAttribute('session');
if (! $session->has(UserInterface::class)) {
return new RedirectResponse('/login');
}
return new TextResponse($this->createJWT());
}
/**
* Validate the supplied JWT Token's payload
*
* @param ServerRequestInterface $request
* @return bool
*/
private function hasValidToken(ServerRequestInterface $request): bool
{
JWT::$leeway = $this->config['leeway'];
$payload = JWT::decode(
$request->getHeaderLine('Bearer'),
$this->config['key'],
$this->config['allowedAlgorithms']
);
$timestamp = (new DateTime())->getTimestamp();
return (
$payload->iss !== $this->config['claim']['iss'] ||
$payload->aud !== $this->config['claim']['aud'] ||
$payload->nbf < $timestamp ||
$payload->exp > $timestamp
);
}
/**
* Create a new JWT
*
* @return ServerRequestInterface
* @throws \Exception
*/
private function createJWT(): string
{
$payload = [
"iss" => $this->config['claim']['iss'],
"aud" => $this->config['claim']['aud'],
"iat" => (new DateTime())->getTimestamp(),
"nbf" => (new DateTime())->getTimestamp(),
"exp" => (new DateTime())->add(new DateInterval($this->config['expiryPeriod']))->getTimestamp(),
];
return JWT::encode($payload, $this->config['key']);
}
}
<?php
// File location: src/App/src/Middleware/Authorization/JWTMiddlewareFactory.php
declare(strict_types=1);
namespace App\Middleware\Authorization;
use Psr\Container\ContainerInterface;
use Psr\Http\Server\MiddlewareInterface;
class JWTMiddlewareFactory
{
public function __invoke(ContainerInterface $container) : MiddlewareInterface
{
$config = $container->has('config') ? $container->get('config') : [];
// Retrieve the configuration listed below.
// I'm not sure if there is, but there might be, a way to enforce the
// configuration element to be present. Need to have a look for that.
return new JWTMiddleware($config['auth']['jwt']);
}
}
@weierophinney
Copy link

@settermjd You also need to note:

  • where to wire the middleware to its factory so that the DI container knows about it
  • where and how to add the middleware either to the application pipeline, a sub-path of the application, or a specific handler

@settermjd
Copy link
Author

Thanks for the feedback, @weierophinney. Working on a simplistic implementation tonight.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment