Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing authentication issue #92

Open
karelVanGeerdeghom opened this issue Jan 8, 2019 · 3 comments
Open

Implementing authentication issue #92

karelVanGeerdeghom opened this issue Jan 8, 2019 · 3 comments

Comments

@karelVanGeerdeghom
Copy link

karelVanGeerdeghom commented Jan 8, 2019

Hey,

I've created a worker service as follows:

voryx.yaml:

services:
    Voryx\ThruwayBundle\Client\ClientManager: '@thruway.client'

    app.service.websocket.worker:
        class: App\Service\Websocket\Worker\WorkerService
        arguments:
            - '@thruway.client'
        tags: ['thruway.resource']

Class:

<?php

namespace App\Service\Websocket\Worker;

use Voryx\ThruwayBundle\Annotation\Register;
use Voryx\ThruwayBundle\Annotation\Subscribe;
use Voryx\ThruwayBundle\Client\ClientManager;

class WorkerService
{
    private $clientManager;

    public function __construct
    (
        ClientManager $clientManager
    )
    {
        $this->clientManager = $clientManager;
    }

    /**
     * @Register("websocket.call")
     */
    public function call($num1, $num2)
    {
        return $num1 + $num2;
    }

    /**
     * @Subscribe("websocket.publish")
     */
    public function publish($value)
    {
        $this->clientManager->publish('websocket.subscription', ['Server responds with: "Hello from server!"']);
    }
}

Client:

var onevent = function(args) {
    console.log(args[0]);
}

session.call('websocket.call', [5, 6])
	.then(
		function (res) {
			console.log(res);
		}
	)
;

session
    .subscribe('websocket.subscription', onevent)
;

session
    .publish('websocket.publish', ['Hello from client!'], {}, {acknowledge: true})
;

When I set authentication to false under voryx_thruway.router, I get the in my console as result '11'. All ok sofar. Also on subscribing to 'websocket.subscription', when I subsequently do the publish, I get the response I'm expecting.

Then I implemnet authentication following this post http://voryx.net/integrating-symfony-authentication-for-thruwaybundle/:

voryx.yaml:

voryx_thruway:
    ...
    router:
        ...
        authentication: true

services:
    app.security.websocket.authentication.provider:
        class: App\Security\Websocket\Authentication\AuthenticationProvider
        arguments:
            - ["@=parameter('voryx_thruway')['realm']"]
            - '@voryx.thruway.loop'
            - '@lexik_jwt_authentication.jwt_manager'
            - '@app.entity.user.repository'
        tags: ['thruway.internal_client']

class

<?php

namespace App\Security\Websocket\Authentication;

use App\Repository\UserRepository;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use React\EventLoop\LoopInterface;
use Thruway\Authentication\AbstractAuthProviderClient;

class AuthenticationProvider extends AbstractAuthProviderClient
{
    private $JWTTokenManager;

    private $userRepository;

    public function __construct
    (
        array $authRealms,
        LoopInterface $loop = null,
        JWTTokenManagerInterface $JWTTokenManager,
        UserRepository $userRepository
    )
    {
        parent::__construct($authRealms, $loop);

        $this->JWTTokenManager = $JWTTokenManager;
        $this->userRepository = $userRepository;
    }

    public function getMethodName()
    {
        return "jwt";
    }

    public function processAuthenticate($signature, $extra = null)
    {
        $token = new JWTUserToken();
        $token->setRawToken($signature);
        $payload = $this->JWTTokenManager->decode($token);
        if ($payload) {
            $user = $this->userRepository->findOneBy(["username" => $payload["username"]]);
            if ($user) {
                $details = [
                    "authid" => $user->getId()
                ];

                return ["SUCCESS", $details];
            }
        }

        return ["FAILURE"];
    }
}

Now authentication works when I create a connection from client side as such:

connection = new autobahn.Connection({
	url: url,
	realm: realm,
        authmethods: ['jwt'],
        onchallenge: function (session, method, extra) {
            return token;
        }
});

As long as my token is still valid, connection succeeds. However, the 'websocket.call' and 'websocket.publish' no longer work. I've put some logging in my worker __construct method, and it logs nothing. When I set voryx_thruway.router.authentication back to false, I get logging and everything works again.

I'm using latest versions of Symfony 4, this bundle and Autobahn.js.

What am I doing wrong?

@karelVanGeerdeghom
Copy link
Author

karelVanGeerdeghom commented Jan 8, 2019

I've gotten a little further:

I've added an authorization manager thus:
voryx.yaml:

voryx_thruway:
    ...
    router:
        ...
        authorization: app.security.websocket.authorization.manager

services:
    app.security.websocket.authorization.manager:
        class: App\Security\Websocket\Authorization\AuthorizationManager
        arguments:
            - "@=parameter('voryx_thruway')['realm']"
            - '@voryx.thruway.loop'

Class:

<?php

namespace App\Security\Websocket\Authorization;

use Thruway\Event\MessageEvent;
use Thruway\Module\RealmModuleInterface;
use Thruway\Module\RouterModuleClient;

class AuthorizationManager extends RouterModuleClient implements RealmModuleInterface
{
    public function getSubscribedRealmEvents()
    {
        return [
            'PublishMessageEvent'   => ['authorize', 100],
            'SubscribeMessageEvent' => ['authorize', 100],
            'RegisterMessageEvent'  => ['authorize', 100],
            'CallMessageEvent'      => ['authorize', 100],
        ];
    }

    public function onSessionStart($session, $transport)
    {
        parent::onSessionStart($session, $transport);

        $session->subscribe('wamp.metaevent.session.on_join', [$this, 'onSessionJoin']);
        $session->subscribe('wamp.metaevent.session.on_leave', [$this, 'onSessionLeave']);
    }

    public function onSessionJoin($args, $kwArgs, $options)
    {
        var_dump('onSessionJoin');
    }

    public function onSessionLeave($args, $kwArgs, $options)
    {
        var_dump('onSessionLeave');
    }

    public function authorize(MessageEvent $msg)
    {
        var_dump($msg);

        return true;
    }
}

I can see the onSessionJoin and onSessionLeave events in my CLI, but when I try to publish, subscribe or call, nothing happens.

@karelVanGeerdeghom
Copy link
Author

karelVanGeerdeghom commented Jan 9, 2019

So adding trusted_url and trusted_port allows me to publish from server to a specific user by authid (ie. user id), thusly:
voryx.yaml:

voryx_thruway:
    ...
    trusted_url: 'ws://<ip>:<port>'
    router:
        ...
        trusted_port: '<port>'

services:
    app.service.websocket.publisher:
        class: App\Service\Websocket\Publisher\PublisherService
        arguments:
            - '@thruway.client'

Class:

<?php

namespace App\Service\Websocket\Publisher;

use Voryx\ThruwayBundle\Client\ClientManager;

class PublisherService
{
    private $clientManager;

    public function __construct
    (
        ClientManager $clientManager
    )
    {
        $this->clientManager = $clientManager;
    }

    public function publish(string $topic, string $message, int $authid)
    {
        $this->clientManager->publish($topic, [$message], [], ['_thruway_eligible_authids' => [$authid]]);
    }
}

@karelVanGeerdeghom
Copy link
Author

karelVanGeerdeghom commented Jan 9, 2019

K, yet more implementation: I can now register when client subscribes (with https://github.com/voryx/ThruwaySubscriptionMeta)

use Thruway\Module\SubscriptionMetaModule;

class AuthorizationManager extends RouterModuleClient implements RealmModuleInterface
{
    ...

    public function onSessionStart($session, $transport)
    {
        ...
        $this->router->getRealmManager()->getRealm('<realm>')->addModule(new SubscriptionMetaModule());

        $session->subscribe('wamp.subscription.on_subscribe', [$this, 'onSubscribe']);
        $session->subscribe('wamp.subscription.on_unsubscribe', [$this, 'onUnsubscribe']);
    }

    ...

    public function onSubscribe($args, $kwArgs, $options)
    {
        var_dump('onSubscribe');
    }

    public function onUnsubscribe($args, $kwArgs, $options)
    {
        var_dump('onUnsubscribe');
    }
}

Still unable to publish and call from client

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant