Я занимаюсь разработкой приложения React / CakePHP с докернизированной средой разработки. Для аутентификации я использую провайдера OpenID Connect для установления личности пользователя, которую затем инкапсулирую в JWT, как предложено в этой статье . Используя плагин CakePHP / Authentication , я перенаправляю запросы из 'https://mydomain.local/' в' https://mydomain.local/login',, который обрабатывает логику OIDC. После проверки подлинности пользователь снова перенаправляется обратно в корень сайта, теперь с JWT в двух файлах cookie.
Моя проблема заключается в том, что запрос зависает при этом окончательном перенаправлении. Если я отключаю перенаправление и вручную возвращаюсь к корню после того, как куки установлены, запрос работает нормально, и мое приложение правильно видит аутентифицированного пользователя через JWT.
Для моей среды разработки я использую Caddyконтейнер как прокси для завершения https и контейнер php-apache для размещения самого приложения. Ни один из журналов сервера не показывает окончательный запрос.
Вот соответствующие части моего кода:
docker_compose.yml:
services:
caddy:
image: "abiosoft/caddy:latest"
volumes:
- ./caddy/certs:/root/certs
- ./caddy/Caddyfile:/etc/Caddyfile
- ./caddy/logs:/var/log
ports:
- "443:2015"
depends_on:
- web
web:
build:
context: .
links:
- db
volumes:
- "./src:/var/www/html/src:rw"
db:
image: mysql:latest
caddy / Caddyfile:
mydomain.local {
log /var/log/access.log
# Mkcert - https://github.com/FiloSottile/mkcert
tls /root/certs/mydomain.local.pem /root/certs/mydomain.local-key.pem
proxy / http://web:80 {
transparent
}
}
src / Application.php:
public function middleware($middlewareQueue)
{
$middlewareQueue
->add(new ErrorHandlerMiddleware(null, Configure::read('Error')))
->add(new AssetMiddleware([
'cacheTime' => Configure::read('Asset.cacheTime')
]))
->add(new RoutingMiddleware($this))
->prepend(new JwtMiddleware())
->add(new AuthenticationMiddleware($this));
return $middlewareQueue;
}
public function getAuthenticationService(ServerRequestInterface $request, ResponseInterface $response)
{
$service = new AuthenticationService([
'unauthenticatedRedirect' => Router::url(['controller' => 'Main', 'action' => 'login']),
'queryParam' => 'redirect',
]);
$service->loadIdentifier('Authentication.JwtSubject', [
'tokenField' => 'id',
'dataField' => 'sub',
'resolver' => 'Authentication.Orm',
]);
$service->loadAuthenticator('Authentication.Jwt', [
'header' => 'Authorization',
'queryParam' => 'token',
'tokenPrefix' => 'Bearer',
'algorithms' => ['HS256'],
'returnPayload' => 'false',
'secretKey' => Security::getSalt(),
]);
return $service;
}
src / Middleware / JwtMiddleware.php:
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\ValidationData;
class JwtMiddleware
{
public function __invoke(RequestInterface $request, ResponseInterface $response, $next)
{
$jwt[0] = $request->getCookie('sa');
$jwt[1] = $request->getCookie('sb');
if (!empty($jwt[0]) && !empty($jwt[1])) {
$data = new ValidationData();
$data->setIssuer('mydomain');
$data->setAudience('mydomain.local');
$data->setId('mydomain');
$jwt = implode('.', $jwt);
$token = (new Parser())->parse((string) $jwt);
if ($token->validate($data)) {
$request = $request->withAddedHeader('Authorization', 'Bearer ' . $jwt);
$response = $response->withCookie((new Cookie('sa'))
->withValue($token->getPayload())
->withExpiry(new \DateTime('+30 minutes'))
->withPath('/')
->withHttpOnly(false)
);
}
}
return $next($request, $response);
}
}
src / Controller / MainController.php:
use Jumbojett\OpenIDConnectClient;
use Jumbojett\OpenIDConnectClientException;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key;
/**
* Main Controller
*
* @property UsersTable $Users
*/
class MainController extends AppController
{
public function beforeFilter(Event $event)
{
$this->Authentication->allowUnauthenticated(['login']);
return parent::beforeFilter($event);
}
/**
* Index method
*
* @return Response|null
*/
public function index()
{
$filePath = WWW_ROOT . '/static.html';
$file = new File($filePath);
$index = $file->read();
$file->close();
return $this->response->withStringBody($index);
}
/**
* Login method
*
* @return Response|null
* @throws OpenIDConnectClientException
*/
public function login()
{
$oidc = new OpenIDConnectClient(
env('OIDC_URL'),
env('OIDC_CLIENT'),
env('OIDC_SECRET')
);
$oidc->addScope('openid');
$oidc->addScope('profile');
$oidc->authenticate();
$this->loadModel('Users');
$user = $this->Users->find()
->where(['auth_id' => $oidc->requestUserInfo('sub')])
->firstOrFail();
$signer = new Sha256();
$time = time();
$token = (new Builder())
->issuedBy('mydomain')
->permittedFor('mydomain.local')
->identifiedBy('mydomain')
->issuedAt($time)
->expiresAt($time + 3600)
->withClaim('sub', $user->id)
->getToken($signer, new Key(Security::getSalt()));
$signature = explode('.', $token->__toString())[2];
$sa = (new Cookie('sa'))
->withValue($token->getPayload())
->withExpiry(new \DateTime('+30 minutes'))
->withPath('/')
->withHttpOnly(false);
$sb = (new Cookie('sb'))
->withValue($signature)
->withPath('/')
->withHttpOnly(true);
$this->response = $this->response
->withCookieCollection(new CookieCollection([$sa, $sb]));
/**** HANG OCCURS ON THIS LINE ****/
return $this->redirect($this->Authentication->getLoginRedirect());
}
}
Любые советы / предложения с благодарностью !!!