Created
September 28, 2018 09:40
-
-
Save jonsa/6c4b6c76bdfaa4b2ce341f684475aec2 to your computer and use it in GitHub Desktop.
Alternative Zend Expressive Session Authentication Workflow
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// in a config/autoload/*.global.php file: | |
declare(strict_types=1); | |
use App\Authentication\LoginAdapter; | |
use Zend\Expressive\Authentication\AuthenticationInterface; | |
use Zend\Expressive\Authentication\UserRepositoryInterface; | |
use Zend\Expressive\Authentication\UserRepository\PdoDatabase; | |
return [ | |
'dependencies' => [ | |
'aliases' => [ | |
AuthenticationInterface::class => LoginAdapter::class, | |
UserRepositoryInterface::class => PdoDatabase::class, | |
], | |
], | |
'authentication' => [ | |
'redirect' => '/login', | |
], | |
]; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// | |
// Authentication on all routes | |
// | |
// in config/pipeline.php: | |
declare(strict_types=1); | |
use Psr\Container\ContainerInterface; | |
use Zend\Expressive\Application; | |
use Zend\Expressive\Authentication\AuthenticationMiddleware; | |
use Zend\Expressive\MiddlewareFactory; | |
return function (Application $app, MiddlewareFactory $factory, ContainerInterface $container) : void { | |
// ... | |
$app->pipe(SessionMiddleware::class); | |
$app->pipe(AuthenticationMiddleware::class); | |
// ... | |
}; | |
// in config/routes.php: | |
declare(strict_types=1); | |
use App\Handler\HomePageHandler; | |
use App\Handler\PingHandler; | |
use Psr\Container\ContainerInterface; | |
use Zend\Expressive\Application; | |
use Zend\Expressive\MiddlewareFactory; | |
return function (Application $app, MiddlewareFactory $factory, ContainerInterface $container) : void { | |
$app->get('/', HomePageHandler::class, 'home'); | |
$app->get('/api/ping', PingHandler::class, 'ping'); | |
}; | |
// | |
// Authentication on specific routes | |
// | |
// in config/pipeline.php: | |
declare(strict_types=1); | |
use Psr\Container\ContainerInterface; | |
use Zend\Expressive\Application; | |
use Zend\Expressive\MiddlewareFactory; | |
return function (Application $app, MiddlewareFactory $factory, ContainerInterface $container) : void { | |
// ... | |
$app->pipe(SessionMiddleware::class); | |
// No AuthenticationMiddleware | |
// ... | |
}; | |
// in config/routes.php: | |
declare(strict_types=1); | |
use Psr\Container\ContainerInterface; | |
use Zend\Expressive\Application; | |
use Zend\Expressive\Authentication\AuthenticationMiddleware; | |
use Zend\Expressive\MiddlewareFactory; | |
return function (Application $app, MiddlewareFactory $factory, ContainerInterface $container) : void { | |
// Tell the router that we accept the login route | |
$loginPath = $container->get('config')['authentication']['redirect']; | |
$app->route($loginPath, AuthenticationMiddleware::class, ['GET', 'POST']); | |
$app->get('/', App\Handler\HomePageHandler::class, 'home'); | |
$app->get('/api/ping', [AuthenticationMiddleware::class, App\Handler\PingHandler::class], 'ping'); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- in src/templates/app/login.phtml: --> | |
<?php $this->layout('layout::default', ['title' => 'Login']) ?> | |
<div class="container"> | |
<div class="row"> | |
<div class="col-sm"> | |
<form action="" method="post"> | |
<?php if (isset($error)): ?> | |
<div class="alert alert-danger" role="alert"> | |
<?= $this->e($error) ?> | |
</div> | |
<?php endif ?> | |
<div class="form-group"> | |
<label for="username">Username</label> | |
<input | |
name="username" | |
value="<?= $username ?>" | |
<?= $username ? '' : 'autofocus' ?> | |
id="username" | |
type="text" | |
class="form-control" | |
placeholder="Enter username" | |
> | |
</div> | |
<div class="form-group"> | |
<label for="password">Password</label> | |
<input | |
name="password" | |
<?= $username ? 'autofocus' : '' ?> | |
id="password" | |
type="password" | |
class="form-control" | |
placeholder="Password" | |
> | |
</div> | |
<button type="submit" class="btn btn-primary">Submit</button> | |
</form> | |
</div> | |
</div> | |
</div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// in src/App/Authentication/LoginAdapter.php: | |
declare(strict_types=1); | |
namespace App\Authentication; | |
use Psr\Http\Message\ResponseInterface; | |
use Psr\Http\Message\ServerRequestInterface; | |
use Psr\Http\Server\RequestHandlerInterface; | |
use Zend\Expressive\Authentication\AuthenticationInterface; | |
use Zend\Expressive\Authentication\UserInterface; | |
use Zend\Expressive\Session\SessionMiddleware; | |
class LoginAdapter implements AuthenticationInterface | |
{ | |
private const REDIRECT_KEY = LoginAdapter::class . ':redirect'; | |
/** | |
* @var RequestHandlerInterface | |
*/ | |
private $handler; | |
/** | |
* @var AuthenticationInterface | |
*/ | |
private $auth; | |
/** | |
* @var string | |
*/ | |
private $path; | |
/** | |
* @var callable | |
*/ | |
private $responseFactory; | |
/** | |
* @param RequestHandlerInterface $handler | |
* @param AuthenticationInterface $auth | |
* @param string $path | |
* @param callable $responseFactory | |
*/ | |
public function __construct( | |
RequestHandlerInterface $handler, | |
AuthenticationInterface $auth, | |
string $path, | |
callable $responseFactory | |
) { | |
$this->handler = $handler; | |
$this->auth = $auth; | |
$this->path = $path; | |
$this->responseFactory = $responseFactory; | |
} | |
/** | |
* Authenticate the PSR-7 request and return a valid user | |
* or null if not authenticated | |
* @param ServerRequestInterface $request | |
* @return null|UserInterface | |
*/ | |
public function authenticate(ServerRequestInterface $request) : ?UserInterface | |
{ | |
$user = $this->auth->authenticate($request); | |
$uri = $request->getUri(); | |
// The user is unauthorized as long as they are on the login route. | |
return $this->path === $uri->getPath() ? null : $user; | |
} | |
/** | |
* Generate the unauthorized response | |
* @param ServerRequestInterface $request | |
* @return ResponseInterface | |
*/ | |
public function unauthorizedResponse(ServerRequestInterface $request) : ResponseInterface | |
{ | |
$session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE); | |
$uri = $request->getUri(); | |
// Redirect to login page | |
if ($this->path !== $uri->getPath()) { | |
$session->set(self::REDIRECT_KEY, (string)$uri); | |
return ($this->responseFactory)() | |
->withHeader('Location', $this->path) | |
->withStatus(302); | |
} | |
// Tell the user to login. | |
if (!$session->has(UserInterface::class)) { | |
return $this->handler->handle($request); | |
} | |
// Redirect back to the url initially requested. | |
$redirectTo = $session->get(self::REDIRECT_KEY); | |
$session->unset(self::REDIRECT_KEY); | |
return ($this->responseFactory)() | |
->withHeader('Location', $redirectTo ?? '/') | |
->withStatus(302); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// in src/App/Authentication/LoginAdapterFactory.php: | |
declare(strict_types=1); | |
namespace App\Authentication; | |
use Psr\Container\ContainerInterface; | |
use Psr\Http\Message\ResponseInterface; | |
use Zend\Expressive\Authentication\Session\PhpSession; | |
class LoginAdapterFactory | |
{ | |
public function __invoke(ContainerInterface $container) : LoginAdapter | |
{ | |
$handler = $container->get(LoginHandler::class); | |
$auth = $container->get(PhpSession::class); | |
$loginPath = $container->get('config')['authentication']['redirect']; | |
$responseFactory = $container->get(ResponseInterface::class); | |
return new LoginAdapter($handler, $auth, $loginPath, $responseFactory); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// in src/App/Authentication/LoginHandler.php: | |
declare(strict_types=1); | |
namespace App\Authentication; | |
use Psr\Http\Message\ResponseInterface; | |
use Psr\Http\Message\ServerRequestInterface; | |
use Psr\Http\Server\RequestHandlerInterface; | |
use Zend\Diactoros\Response\HtmlResponse; | |
use Zend\Expressive\Template\TemplateRendererInterface; | |
class LoginHandler implements RequestHandlerInterface | |
{ | |
/** | |
* @var TemplateRendererInterface | |
*/ | |
private $renderer; | |
/** | |
* @param TemplateRendererInterface $renderer | |
*/ | |
public function __construct(TemplateRendererInterface $renderer) | |
{ | |
$this->renderer = $renderer; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function handle(ServerRequestInterface $request) : ResponseInterface | |
{ | |
$post = (array)$request->getParsedBody() ?? []; | |
$data = [ | |
'username' => $post['username'] ?? '', | |
'password' => $post['password'] ?? '', | |
]; | |
if ($request->getMethod() === 'POST') { | |
$data['error'] = 'Invalid username or password'; | |
} | |
return new HtmlResponse($this->renderer->render('app::login', $data)); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// in src/App/Authentication/LoginHandlerFactory.php: | |
declare(strict_types=1); | |
namespace App\Authentication; | |
use Psr\Container\ContainerInterface; | |
use Zend\Expressive\Template\TemplateRendererInterface; | |
class LoginHandlerFactory | |
{ | |
public function __invoke(ContainerInterface $container) : LoginHandler | |
{ | |
$template = $container->get(TemplateRendererInterface::class); | |
return new LoginHandler($template); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// in config/autoload/zend-expressive-tooling-factories.global.php: | |
declare(strict_types=1); | |
use App\Authentication\LoginAdapter::class; | |
use App\Authentication\LoginAdapterFactory::class; | |
use App\Authentication\LoginHandler::class; | |
use App\Authentication\LoginHandlerFactory::class; | |
return [ | |
'dependencies' => [ | |
'factories' => [ | |
LoginAdapter::class => LoginAdapterFactory::class, | |
LoginHandler::class => LoginHandlerFactory::class, | |
], | |
], | |
]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment