-
Hi, i changed all to the new api. works for javascript clients. they are authentificated. final class Client implements LoggerAwareInterface
{
use LoggerAwareTrait;
private const MSG_WELCOME = 0;
private const MSG_PREFIX = 1;
private const MSG_CALL = 2;
private const MSG_CALL_RESULT = 3;
private const MSG_CALL_ERROR = 4;
private const MSG_SUBSCRIBE = 5;
private const MSG_UNSUBSCRIBE = 6;
private const MSG_PUBLISH = 7;
private const MSG_EVENT = 8;
private WampRouter $router;
private PawlClient $client;
public function __construct(WampRouter $router, PawlClient $client)
{
$this->router = $router;
$this->client = $client;
}
/**
* Calls a RPC handler on the websocket server.
*
* @param array $payload
* @param string $routeName
* @param array $routeParameters
*/
public function call(array $payload, string $routeName, array $routeParameters = []): void
{
$this->client->connect()
->then(
function (WebSocket $connection) use ($payload, $routeName, $routeParameters) {
$this->logger->debug('Client connection resolved');
$route = $this->router->generate($routeName, $routeParameters);
$this->logger->debug('Calling RPC function on websocket server', ['route' => $route, 'payload' => $payload]);
try {
$message = json_encode(array_merge([self::MSG_CALL, uniqid('', true), $route], $payload), JSON_THROW_ON_ERROR);
$connection->send($message);
} catch (JsonException $exception) {
$this->logger->error('Could not encode message to call RPC function on websocket server.', ['exception' => $exception]);
throw $exception;
} finally {
$connection->close();
}
},
function (Throwable $throwable): void {
$this->logger->error('Client connection rejected', ['exception' => $throwable]);
}
);
}
/**
* Publishes to a Topic on the websocket server.
*
* @param array $payload
* @param string $routeName
* @param array $routeParameters
*/
public function publish(array $payload, string $routeName, array $routeParameters = []): void
{
$this->client->connect()
->then(
function (WebSocket $connection) use ($payload, $routeName, $routeParameters) {
$this->logger->debug('Client connection resolved');
$route = $this->router->generate($routeName, $routeParameters);
$this->logger->debug('Publishing message to websocket server', ['route' => $route, 'payload' => $payload]);
try {
$message = json_encode([self::MSG_PUBLISH, $route, $payload, [], []], JSON_THROW_ON_ERROR);
$connection->send($message);
} catch (JsonException $exception) {
$this->logger->error('Could not encode message to publish to websocket server.', ['exception' => $exception]);
throw $exception;
} finally {
$connection->close();
}
},
function (Throwable $throwable): void {
$this->logger->error('Client connection rejected', ['exception' => $throwable]);
}
);
}
} i used this service made everywhere i needed it an instance and -> publish() |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
For server-to-server calls like these, you aren't going to be able to authenticate as a user within your app unless you pass some info along in the connect call from the In the Symfony app for one of my clients, this is essentially what our <?php declare(strict_types=1);
namespace App\Websocket\Client;
use Ratchet\Client;
use React\Promise\PromiseInterface;
/**
* Thin wrapper around the `Ratchet\Client\connect()` function to allow for unit testing.
*/
class PawlClient
{
private WebsocketUriBuilder $uriBuilder;
private string $secret;
public function __construct(WebsocketUriBuilder $uriBuilder, string $secret)
{
$this->uriBuilder = $uriBuilder;
$this->secret = $secret;
}
public function connect(): PromiseInterface
{
return Client\connect($this->uriBuilder->getServerUri(), ['wamp'], ['X-Secret-Header-With-Token' => hash('sha1', $this->secret)]);
}
} Essentially we're sending a SHA1 hash of <?php declare(strict_types=1);
namespace App\Websocket\Server;
use GuzzleHttp\Psr7\Message;
use GuzzleHttp\Psr7\Response;
use Psr\Http\Message\RequestInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Ratchet\ConnectionInterface;
use Ratchet\Http\HttpServerInterface;
class ServerToServerRequestValidator implements HttpServerInterface, LoggerAwareInterface
{
use LoggerAwareTrait;
private HttpServerInterface $app;
private string $secret;
public function __construct(HttpServerInterface $app, string $secret)
{
$this->app = $app;
$this->secret = $secret;
}
/**
* @return mixed
*/
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null)
{
if (null === $request) {
throw new \UnexpectedValueException('$request can not be null');
}
$conn->tokenAuth = false;
$this->logger?->debug('Negotiating websocket request: ' . Message::toString($request));
$response = null;
// If the request has our token header, and the token does not pass validation, then issue a 403 and immediately reject the connection
if ($request->hasHeader('X-Secret-Header-With-Token')) {
if (!hash_equals(hash('sha1', $this->secret), $request->getHeaderLine('X-Secret-Header-With-Token'))) {
$response = new Response(403, ['Content-Type' => 'text/plain; charset=utf8'], 'Access Denied', '1.1');
} else {
$conn->tokenAuth = true;
}
}
// If there is no response, then we can proceed down the decorator stack
if ($response === null) {
return $this->app->onOpen($conn, $request);
}
// Send our response and close the connection
$response = $response->withHeader('X-Powered-By', \Ratchet\VERSION);
$this->logger?->debug('Sending websocket response: ' . Message::toString($response));
$conn->send(Message::toString($response));
return $conn->close();
}
/**
* @param string $msg
*
* @return mixed
*/
public function onMessage(ConnectionInterface $from, $msg)
{
return $this->app->onMessage($from, $msg);
}
/**
* @return mixed
*/
public function onClose(ConnectionInterface $conn)
{
return $this->app->onClose($conn);
}
/**
* @return mixed
*/
public function onError(ConnectionInterface $conn, \Exception $e)
{
return $this->app->onError($conn, $e);
}
} If you need those server-to-server calls to authenticate as a specific user, you're going to need to tweak the API design a bit more to pass the user info along somehow (probably another request header, which I'd suggest would mean adding a |
Beta Was this translation helpful? Give feedback.
-
I used your suggestion but i resolved it on a bit different way. In My SecureTopicHandler <?php
class SecureTopicHandler
{
public function secure(?ConnectionInterface $conn, Topic $topic, WampRequest $request, $payload = null, ?array $exclude = [], ?array $eligible = null, ?string $provider = null): void
{
/** @var $serverRequest \GuzzleHttp\Psr7\Request */
$serverRequest = $conn->__get('httpRequest');
// check if user authorized
$user = $this->clientManipulator->getUser($conn);
if(!$user instanceof User){
// if not check if its a server internal request.
if($serverRequest->hasHeader('xxxxToken') && $this->container->hasParameter('websocket_secret')){
$token = $serverRequest->getHeader('xxxxToken');
if(!hash_equals(hash('sha1', $this->container->getParameter('websocket_secret')), $token[0])){
throw new FirewallRejectionException('Access denied');
}
}else{
throw new FirewallRejectionException('Access denied');
}
}
}
} works for now. guess this is fine. |
Beta Was this translation helpful? Give feedback.
I used your suggestion but i resolved it on a bit different way.
In My SecureTopicHandler