Open ndoulgeridis opened 7 years ago
Yes you can, you have to bring your own authenticationProvider, here is the default provided by this bundle : https://github.com/GeniusesOfSymfony/WebSocketBundle/blob/master/Client/Auth/WebsocketAuthenticationProvider.php (using shared session)
So you can create a jwt implementation as well.
Hi;
I'm looking to use this functionnality but i don't know how i can do for create new authentificationProvider.. If you can help me it will be wonderfull! Actually :
I'm using FOSUserBundle, LexikJWTAuthenticationBundle.
When i'm on website, when i'm connection the websocket everything works good. User are authentificated :
[2017-03-10 06:30:18] websocket.INFO: xxx@xxx.xx connected {"connection_id":5029,"session_id":"191374500458c28e4a506de469327892","storage_id":5029}
Executed once every 5 seconds
[2017-03-10 06:30:21] websocket.DEBUG: GET CLIENT 5029
When i'm logged by the api (json web token), i have this :
[2017-03-10 06:30:31] websocket.INFO: anon-38388862858c28e5721e11572010159 disconnected {"connection_id":5106,"session_id":"38388862858c28e5721e11572010159","storage_id":5106,"username":"anon-38388862858c28e5721e11572010159"}
I have try to add api firewall in this config, but it's the same.
gos_web_socket:
client:
firewall: [main, api] #can be an array of firewalls
session_handler: "@session.handler.pdo.service"
server:
port: "%gos_web_socket_port%" #The port the socket server will listen on
host: "%gos_web_socket_host%" #The host ip to bind to
router:
resources:
- "@jokari4242ChatBundle/Resources/config/routing.yml"
origin_check: true
topics:
- "@chat_service"
Thanks for your help,
Antoine
Hi, I already have a custom auth provider, but how to link this with gosWebSocket ?
@jokari4242 If I find a solution, I will explain it on this issue.
You would have to override gos_websocket.websocket_authentification.provider service in compiler pass completely to do that as it seems to be rather not replaceable by simple means of config.
@rsaenen can you show us how you solve this problem
@rsaenen can you show us how you solve this problem
Sorry I don't remember and I switched to node
Hey guys, I was looking for solving of this case, and finally created my own implementation of JWT Auth with this bundle. I store my JWT Token in cookie and I can access it from Request object inside Connection.
I've created WebSocketJWTAuthProvider class which looks like:
<?php declare(strict_types=1);
namespace App\Shared\Providers;
use Gos\Bundle\WebSocketBundle\Client\Auth\WebsocketAuthenticationProviderInterface;
use Gos\Bundle\WebSocketBundle\Client\ClientStorageInterface;
use Illuminate\Support\Collection;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken;
use Lexik\Bundle\JWTAuthenticationBundle\Security\User\JWTUserProvider;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWSProvider\JWSProviderInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Ratchet\ConnectionInterface;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
final class WebSocketJWTAuthProvider implements WebsocketAuthenticationProviderInterface, LoggerAwareInterface
{
use LoggerAwareTrait;
/**
* @var ClientStorageInterface
*/
private ClientStorageInterface $clientStorage;
/**
* @var JWTTokenManagerInterface
*/
private JWTTokenManagerInterface $tokenManager;
/**
* @var JWSProviderInterface
*/
private JWSProviderInterface $JWSProvider;
/**
* @var UserProviderInterface|JWTUserProvider
*/
private UserProviderInterface $jwtUserProvider;
/**
* @param ClientStorageInterface $clientStorage
* @param JWTUserProvider $JWTUserProvider
* @param JWSProviderInterface $JWSProvider
*/
public function __construct(ClientStorageInterface $clientStorage,
UserProviderInterface $JWTUserProvider,
JWSProviderInterface $JWSProvider
)
{
$this->clientStorage = $clientStorage;
$this->JWSProvider = $JWSProvider;
$this->jwtUserProvider = $JWTUserProvider;
}
public function authenticate(ConnectionInterface $conn): TokenInterface
{
if (1 === \count($this->firewalls) && 'ws_firewall' === $this->firewalls[0]) {
if (null !== $this->logger) {
$this->logger->warning(
sprintf(
'User firewall is not configured, we have set %s by default',
$this->firewalls[0]
)
);
}
}
$loggerContext = [
'connection_id' => $conn->resourceId,
'session_id' => $conn->WAMP->sessionId,
];
$token = $this->getToken($conn);
$identifier = $this->clientStorage->getStorageId($conn);
$loggerContext['storage_id'] = $identifier;
$this->clientStorage->addClient($identifier, $token);
if (null !== $this->logger) {
$this->logger->info(
sprintf(
'%s connected',
$token->getUsername()
),
$loggerContext
);
}
return $token;
}
// @TODO: CLEAN UP HERE
private function getToken(ConnectionInterface $connection): TokenInterface
{
$token = null;
/** @var RequestInterface $req */
$req = $connection->httpRequest;
$cookies = explode('; ', $req->getHeader('Cookie')[0]);
$cookiesCollection = new Collection();
foreach ($cookies as $cookie) {
$cookieArray = explode('=', $cookie);
[$name, $value] = $cookieArray;
$cookiesCollection->offsetSet($name, $value);
}
$rawToken = $cookiesCollection->get('BEARER');
try {
$jws = $this->JWSProvider->load($rawToken);
$user = $this->jwtUserProvider->loadUserByUsername($jws->getPayload()['username']);
$token = new JWTUserToken(
user: $user,
rawToken: $rawToken
);
} catch (JWTDecodeFailureException $decodeFailureException) {
$token = new AnonymousToken(' main', 'anon-'.$connection->WAMP->sessionId);
}
return $token;
}
}
in services.yaml:
gos_web_socket.client.authentication.websocket_provider:
class: App\Shared\Providers\WebSocketJWTAuthProvider
public: false
arguments:
- '@gos_web_socket.client.storage'
calls:
- [ setLogger, ['@logger'] ]
tags:
- { name: monolog.logger, channel: websocket }
now you will be able to access your user in topic like this:
class ActivityStatus implements TopicInterface
{
public function __construct(
private MessageBusInterface $messageBus,
private ClientManipulatorInterface $clientManipulator,
private LoggerInterface $logger,
)
{
}
/**
* This will receive any Subscription requests for this topic.
*
* @param ConnectionInterface $connection
* @param Topic $topic
* @param WampRequest $request
*
* @return void
*/
public function onSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request)
{
/** @var JWTUserToken $user */
$token = $this->clientManipulator->getClient($connection);
if (empty($token->getUser())) {
throw new FirewallRejectionException('User not authenticated to subscribe topic');
}
$this->messageBus->dispatch(
new SetProfileOnlineCommand($token->getUser()->getId())
);
}
/**
*
* @return string
*/
public function getName(): string
{
return 'account.user.activity_status';
}
}
I hope it helps someone who had similiar problem.
@astrocodespace thank you, it's help me. it's time for me to make some upgrades on my app
Hey guys, I was looking for solving of this case, and finally created my own implementation of JWT Auth with this bundle. I store my JWT Token in cookie and I can access it from Request object inside Connection.
I've created WebSocketJWTAuthProvider class which looks like:
<?php declare(strict_types=1); namespace App\Shared\Providers; use Gos\Bundle\WebSocketBundle\Client\Auth\WebsocketAuthenticationProviderInterface; use Gos\Bundle\WebSocketBundle\Client\ClientStorageInterface; use Illuminate\Support\Collection; use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException; use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken; use Lexik\Bundle\JWTAuthenticationBundle\Security\User\JWTUserProvider; use Lexik\Bundle\JWTAuthenticationBundle\Services\JWSProvider\JWSProviderInterface; use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface; use Psr\Http\Message\RequestInterface; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; use Ratchet\ConnectionInterface; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; final class WebSocketJWTAuthProvider implements WebsocketAuthenticationProviderInterface, LoggerAwareInterface { use LoggerAwareTrait; /** * @var ClientStorageInterface */ private ClientStorageInterface $clientStorage; /** * @var JWTTokenManagerInterface */ private JWTTokenManagerInterface $tokenManager; /** * @var JWSProviderInterface */ private JWSProviderInterface $JWSProvider; /** * @var UserProviderInterface|JWTUserProvider */ private UserProviderInterface $jwtUserProvider; /** * @param ClientStorageInterface $clientStorage * @param JWTUserProvider $JWTUserProvider * @param JWSProviderInterface $JWSProvider */ public function __construct(ClientStorageInterface $clientStorage, UserProviderInterface $JWTUserProvider, JWSProviderInterface $JWSProvider ) { $this->clientStorage = $clientStorage; $this->JWSProvider = $JWSProvider; $this->jwtUserProvider = $JWTUserProvider; } public function authenticate(ConnectionInterface $conn): TokenInterface { if (1 === \count($this->firewalls) && 'ws_firewall' === $this->firewalls[0]) { if (null !== $this->logger) { $this->logger->warning( sprintf( 'User firewall is not configured, we have set %s by default', $this->firewalls[0] ) ); } } $loggerContext = [ 'connection_id' => $conn->resourceId, 'session_id' => $conn->WAMP->sessionId, ]; $token = $this->getToken($conn); $identifier = $this->clientStorage->getStorageId($conn); $loggerContext['storage_id'] = $identifier; $this->clientStorage->addClient($identifier, $token); if (null !== $this->logger) { $this->logger->info( sprintf( '%s connected', $token->getUsername() ), $loggerContext ); } return $token; } // @TODO: CLEAN UP HERE private function getToken(ConnectionInterface $connection): TokenInterface { $token = null; /** @var RequestInterface $req */ $req = $connection->httpRequest; $cookies = explode('; ', $req->getHeader('Cookie')[0]); $cookiesCollection = new Collection(); foreach ($cookies as $cookie) { $cookieArray = explode('=', $cookie); [$name, $value] = $cookieArray; $cookiesCollection->offsetSet($name, $value); } $rawToken = $cookiesCollection->get('BEARER'); try { $jws = $this->JWSProvider->load($rawToken); $user = $this->jwtUserProvider->loadUserByUsername($jws->getPayload()['username']); $token = new JWTUserToken( user: $user, rawToken: $rawToken ); } catch (JWTDecodeFailureException $decodeFailureException) { $token = new AnonymousToken(' main', 'anon-'.$connection->WAMP->sessionId); } return $token; } }
in services.yaml:
gos_web_socket.client.authentication.websocket_provider: class: App\Shared\Providers\WebSocketJWTAuthProvider public: false arguments: - '@gos_web_socket.client.storage' calls: - [ setLogger, ['@logger'] ] tags: - { name: monolog.logger, channel: websocket }
now you will be able to access your user in topic like this:
class ActivityStatus implements TopicInterface { public function __construct( private MessageBusInterface $messageBus, private ClientManipulatorInterface $clientManipulator, private LoggerInterface $logger, ) { } /** * This will receive any Subscription requests for this topic. * * @param ConnectionInterface $connection * @param Topic $topic * @param WampRequest $request * * @return void */ public function onSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request) { /** @var JWTUserToken $user */ $token = $this->clientManipulator->getClient($connection); if (empty($token->getUser())) { throw new FirewallRejectionException('User not authenticated to subscribe topic'); } $this->messageBus->dispatch( new SetProfileOnlineCommand($token->getUser()->getId()) ); } /** * * @return string */ public function getName(): string { return 'account.user.activity_status'; } }
I hope it helps someone who had similiar problem.
Thank you very much for example.
Hello, is it possible to AUTH the user on socket connect with a JWT token? Something like socketio-jwt does.
I use lexik/LexikJWTAuthenticationBundle for JWT generation and security handling.