Запрос зависает при перенаправлении после аутентификации - PullRequest
0 голосов
/ 05 октября 2019

Я занимаюсь разработкой приложения 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());
    }
}

Любые советы / предложения с благодарностью !!!

1 Ответ

0 голосов
/ 05 октября 2019

Проблема заключалась в том, что перенаправление было небезопасным, поскольку сервер приложений выполнял HTTP (с SSL, прерванным на прокси-сервере). Изменение последней строки login() в MainController.php на

return $this->redirect(Router::url('/', true)); // generate full URL

и установка fullBaseUrl в config/app.php на '<a href="https://mydomain.local" rel="nofollow noreferrer">https://mydomain.local</a>' устранили проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...