Skip to content

Commit a0df9fa

Browse files
committed
Merge branch 'new-auth' into 3.x
2 parents 12577b2 + 223b994 commit a0df9fa

File tree

59 files changed

+3522
-189
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+3522
-189
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 3.11.0 (????-??-??)
4+
5+
- Implemented a new [authentication API](docs/authentication.md) and deprecated the previous implementation
6+
37
## 3.10.0 (2021-07-31)
48

59
- Add support for `psr/log` 2.0 and 3.0

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"symfony/event-dispatcher": "^4.4 || ^5.3",
3131
"symfony/http-foundation": "^4.4 || ^5.3",
3232
"symfony/http-kernel": "^4.4 || ^5.3",
33+
"symfony/polyfill-php80": "^1.15",
3334
"symfony/security-core": "^4.4 || ^5.3",
3435
"symfony/serializer": "^4.4 || ^5.3",
3536
"symfony/yaml": "^4.4 || ^5.3"

config/aliases.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
services:
2+
Gos\Bundle\WebSocketBundle\Authentication\AuthenticatorInterface:
3+
alias: gos_web_socket.authentication.authenticator
4+
5+
Gos\Bundle\WebSocketBundle\Authentication\ConnectionRepositoryInterface:
6+
alias: gos_web_socket.authentication.connection_repository
7+
8+
Gos\Bundle\WebSocketBundle\Authentication\Storage\TokenStorageInterface:
9+
alias: gos_web_socket.authentication.token_storage
10+
211
Gos\Bundle\WebSocketBundle\Client\Auth\WebsocketAuthenticationProviderInterface:
312
alias: gos_web_socket.client.authentication.websocket_provider
13+
# deprecated alias, deprecation is set in the container extension
414

515
Gos\Bundle\WebSocketBundle\Client\ClientManipulatorInterface:
616
alias: gos_web_socket.client.manipulator
17+
# deprecated alias, deprecation is set in the container extension
718

819
Gos\Bundle\WebSocketBundle\Client\ClientStorageInterface:
920
alias: gos_web_socket.client.storage
21+
# deprecated alias, deprecation is set in the container extension
1022

1123
Gos\Bundle\WebSocketBundle\Pusher\PusherRegistry:
1224
alias: gos_web_socket.registry.pusher

config/services.yaml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,46 @@
11
services:
2+
gos_web_socket.authentication.authenticator:
3+
class: Gos\Bundle\WebSocketBundle\Authentication\Authenticator
4+
public: false
5+
arguments:
6+
- ~ # authentication providers, set based on configuration
7+
- '@gos_web_socket.authentication.token_storage'
8+
9+
gos_web_socket.authentication.connection_repository:
10+
class: Gos\Bundle\WebSocketBundle\Authentication\ConnectionRepository
11+
public: false
12+
arguments:
13+
- '@gos_web_socket.authentication.token_storage'
14+
- '@gos_web_socket.authentication.authenticator'
15+
16+
gos_web_socket.authentication.provider.session:
17+
class: Gos\Bundle\WebSocketBundle\Authentication\Provider\SessionAuthenticationProvider
18+
abstract: true
19+
public: false
20+
arguments:
21+
- '@gos_web_socket.authentication.token_storage'
22+
- ~ # firewalls, set based on configuration
23+
24+
gos_web_socket.authentication.storage.driver.in_memory:
25+
class: Gos\Bundle\WebSocketBundle\Authentication\Storage\Driver\InMemoryStorageDriver
26+
public: false
27+
28+
gos_web_socket.authentication.storage.driver.psr_cache:
29+
class: Gos\Bundle\WebSocketBundle\Authentication\Storage\Driver\PsrCacheStorageDriver
30+
public: false
31+
arguments:
32+
- ~ # cache pool
33+
34+
gos_web_socket.authentication.token_storage:
35+
class: Gos\Bundle\WebSocketBundle\Authentication\Storage\TokenStorage
36+
public: false
37+
arguments:
38+
- '@gos_web_socket.authentication.storage.driver'
39+
240
gos_web_socket.client.authentication.websocket_provider:
341
class: Gos\Bundle\WebSocketBundle\Client\Auth\WebsocketAuthenticationProvider
442
public: false
43+
# deprecated service, deprecation is set in the container extension
544
arguments:
645
- '@gos_web_socket.client.storage'
746
- '%gos_web_socket.firewall%'
@@ -13,20 +52,24 @@ services:
1352
gos_web_socket.client.driver.doctrine_cache:
1453
class: Gos\Bundle\WebSocketBundle\Client\Driver\DoctrineCacheDriverDecorator
1554
public: false
55+
# deprecated service, deprecation is set in the container extension
1656
# arguments are set by the container extension
1757

1858
gos_web_socket.client.driver.symfony_cache:
1959
class: Gos\Bundle\WebSocketBundle\Client\Driver\SymfonyCacheDriverDecorator
2060
public: false
61+
# deprecated service, deprecation is set in the container extension
2162
# arguments are set by the container extension
2263

2364
gos_web_socket.client.driver.in_memory:
2465
class: Gos\Bundle\WebSocketBundle\Client\Driver\InMemoryDriver
2566
public: false
67+
# deprecated service, deprecation is set in the container extension
2668

2769
gos_web_socket.client.manipulator:
2870
class: Gos\Bundle\WebSocketBundle\Client\ClientManipulator
2971
public: true
72+
# deprecated service, deprecation is set in the container extension
3073
arguments:
3174
- '@gos_web_socket.client.storage'
3275
- '@gos_web_socket.client.authentication.websocket_provider'
@@ -36,6 +79,7 @@ services:
3679
gos_web_socket.client.storage:
3780
class: Gos\Bundle\WebSocketBundle\Client\ClientStorage
3881
public: false
82+
# deprecated service, deprecation is set in the container extension
3983
arguments:
4084
- ~ # driver argument is set by the container extension
4185
- '%gos_web_socket.client.storage.ttl%'

docs/auth.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
# Authenticating Users
1+
# Authenticating Users (Legacy Authentication)
2+
3+
**NOTE** This guide covers the legacy authentication system which will be removed in GosWebSocketBundle 4.0, please see [this guide](authentication.md) for information on the new authentication system.
24

35
When a connection is opened to the websocket server, the user is authenticated against the firewall(s) you have configured the bundle to use from your application.
46

docs/authentication.md

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
# Authenticating Users
2+
3+
The websocket bundle automatically handles authenticating users on every connection to your websocket server, allowing your [RPC](rpc.md) and [Topic](topics.md) classes to use your authenticated user data.
4+
5+
## Enabling the Authenticator API
6+
7+
The following is the minimum configuration needed to enable the authenticator and its services:
8+
9+
```yaml
10+
gos_web_socket:
11+
authentication:
12+
enable_authenticator: true
13+
```
14+
15+
This instructs the bundle to enable the new authentication API and replace the [legacy implementation](auth.md). When this system is enabled, the legacy services are removed from the service container and should not be used due to incompatibilities between the two authentication APIs.
16+
17+
*NOTE* This configuration is needed as a temporary migration step, as of GosWebSocketBundle 4.0 this will be the authentication API.
18+
19+
However, this will not enable any authentication providers on its own, you must also configure the provider(s) your application will use for authentication.
20+
21+
## Authentication Providers
22+
23+
An authentication provider is an implementation of `Gos\Bundle\WebSocketBundle\Authentication\Provider\AuthenticationProviderInterface` which processes a `Ratchet\ConnectionInterface` and creates a security token representing the current user.
24+
25+
A provider is required to have two methods:
26+
27+
- `supports()` - Determines if the provider can authenticate the given connection
28+
- `authenticate()` - Authenticates the connection
29+
30+
### Session Authentication
31+
32+
The bundle provides a session authentication provider which will authenticate users using their HTTP session from your website's frontend.
33+
34+
To enable the session authenticator, you must add it to the `providers` list in the authentication configuration and configure the session handler that will be used. In this example, your Symfony application will use the PDO session handler.
35+
36+
```yaml
37+
services:
38+
session.handler.pdo:
39+
class: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
40+
arguments:
41+
- !service { class: PDO, factory: ['@database_connection', 'getWrappedConnection'] }
42+
- { lock_mode: 0 }
43+
44+
framework:
45+
session:
46+
handler_id: '@session.handler.pdo'
47+
48+
gos_web_socket:
49+
authentication:
50+
enable_authenticator: true
51+
providers:
52+
session:
53+
session_handler: '@session.handler.pdo'
54+
```
55+
56+
Configuring the session handler will add the [`SessionProvider` component](http://socketo.me/docs/sessions) to the websocket server which will provide a read-only interface for the session data from your website. Note that there are some restrictions on the session handlers you can use, please see the linked documentation for more information.
57+
58+
By default, the session authentication provider will attempt to authenticate to any of the firewalls set in your `security.firewalls` configuration in the same order which the firewalls are defined. You can specify the firewall(s) to use with the `firewall` configuration key on the session provider.
59+
60+
```yaml
61+
gos_web_socket:
62+
authentication:
63+
enable_authenticator: true
64+
providers:
65+
session:
66+
firewalls: ['main'] # This can be an array to specify multiple firewalls or a string when specifying a single firewall
67+
session_handler: '@session.handler.pdo'
68+
```
69+
70+
### Provider Priority
71+
72+
When providers are registered to the authenticator service, they are then used in a "first in, first out" order, meaning the order they are triggered will be the same order they are configured in. Assuming your application has multiple authenticators and you want a custom authenticator to be attempted before the session authenticator, you would use the below configuration to do so:
73+
74+
```yaml
75+
gos_web_socket:
76+
authentication:
77+
enable_authenticator: true
78+
providers:
79+
custom: ~
80+
session: ~
81+
```
82+
83+
### Registering New Authenticators
84+
85+
In addition to creating a class implementing `Gos\Bundle\WebSocketBundle\Authentication\Provider\AuthenticationProviderInterface`, you must also register the authenticator with a `Gos\Bundle\WebSocketBundle\DependencyInjection\Factory\Authentication\AuthenticationProviderFactoryInterface` to the bundle's container extension. Similar to factories used by Symfony's `SecurityBundle`, this factory is used to allow you to configure the authenticator for your application and build the authentication provider service.
86+
87+
A factory is required to have two methods:
88+
89+
- `getKey()` - A unique name to identify the provider in the application configuration, this name is used as the key in the `providers` list
90+
- `addConfiguration()` - Defines the configuration nodes (if any are required) for the authenticator
91+
- `createAuthenticationProvider()` - Registers the authentication provider service to the dependency injection container and returns the provider's service ID
92+
93+
The factory must be registered to this bundle's container extension when the container is being built. Typically, this would be in the `build()` method of your application's `Kernel` or a bundle's `Bundle` class.
94+
95+
```php
96+
<?php
97+
98+
namespace App;
99+
100+
use App\DependencyInjection\Factory\Authentication\CustomAuthenticationProviderFactory;
101+
use Gos\Bundle\WebSocketBundle\DependencyInjection\GosWebSocketExtension;
102+
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
103+
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
104+
105+
class Kernel extends BaseKernel
106+
{
107+
use MicroKernelTrait;
108+
109+
protected function build(ContainerBuilder $container): void
110+
{
111+
/** @var GosWebSocketExtension $extension */
112+
$extension = $container->getExtension('gos_web_socket');
113+
$extension->addAuthenticationProviderFactory(new CustomAuthenticationProviderFactory());
114+
}
115+
}
116+
```
117+
118+
## Storing Tokens
119+
120+
After authentication is complete, the token is stored in a `Gos\Bundle\WebSocketBundle\Authentication\Storage\TokenStorageInterface` instance. The default implementation uses a `Gos\Bundle\WebSocketBundle\Authentication\Storage\Driver\StorageDriverInterface` as an abstraction layer for where authentication tokens are stored.
121+
122+
By default, the bundle uses an in-memory storage driver. The storage driver can be configured with the `storage` section of the authentication configuration.
123+
124+
### In-Memory Storage
125+
126+
The below example represents the default configuration for the in-memory storage driver.
127+
128+
```yaml
129+
gos_web_socket:
130+
authentication:
131+
storage:
132+
type: in_memory
133+
```
134+
135+
### Cache Storage
136+
137+
A cache pool can be used as a storage driver by setting the storage type to `psr_cache` and specifying the cache pool that should be used.
138+
139+
*NOTE* Unlike the legacy authentication system, there are no options to configure a storage TTL or cache prefix in the bundle configuration. These should be set on your cache pool if desired.
140+
141+
```yaml
142+
gos_web_socket:
143+
authentication:
144+
storage:
145+
type: psr_cache
146+
pool: 'cache.websocket'
147+
```
148+
149+
### Service Storage
150+
151+
You can create your own implementation of the storage driver interface and use that service by setting the storage type to `service` and specifying the container service ID to use.
152+
153+
```yaml
154+
gos_web_socket:
155+
authentication:
156+
storage:
157+
type: storage
158+
id: 'app.websocket.storage.driver'
159+
```
160+
161+
## Fetching Tokens
162+
163+
The `Gos\Bundle\WebSocketBundle\Authentication\ConnectionRepositoryInterface` provides several helper methods for querying the token storage to find the connections and tokens for any connected user. For example, this repository could be used to find all authenticated users connected to a given topic to send a message.
164+
165+
### Token Connection DTO
166+
167+
The `Gos\Bundle\WebSocketBundle\Authentication\TokenConnection` object is a DTO which is returned by many of the repository methods and contains the `Ratchet\ConnectionInterface` and its security token from the authenticator.
168+
169+
### Retrieving All Connections For A Topic
170+
171+
The `findAll()` method is used to find all connections for a given topic. The method has an optional `$anonymous` parameter which can be used to filter out connections for unauthenticated users. The list will be returned as an array of `Gos\Bundle\WebSocketBundle\Authentication\TokenConnection` objects.
172+
173+
### Retrieving All Connections For A Username
174+
175+
The `findAllByUsername()` method is used to find all connections for a user with the given username. This is helpful if a user has multiple active connections (i.e. has multiple tabs in their browser open). The list will be returned as an array of `Gos\Bundle\WebSocketBundle\Authentication\TokenConnection` objects.
176+
177+
### Retrieving All Connections For A User With A Role
178+
179+
The `findAllWithRoles()` method is used to find all connections for a user who has any of the given roles. Note that this method checks the list of roles on the underlying security token and does not use the site's role hierarchy. The list will be returned as an array of `Gos\Bundle\WebSocketBundle\Authentication\TokenConnection` objects.
180+
181+
### Retrieving The Token For A Connection
182+
183+
The `findTokenForConnection()` method is used to find the security token for the given connection.
184+
185+
### Retrieving The User For A Connection
186+
187+
The `getUser()` method is used to retrieve the user for the given connection. This is a shortcut for `$repository->findTokenForConnection($token)->getUser()`.
188+
189+
## Migrating from the Legacy Authentication API
190+
191+
To update your application to use the new authentication API, you will need to make the following changes:
192+
193+
1) Enable the new API
194+
195+
The below is the minimal configuration necessary with notes regarding migrating from the legacy configuration:
196+
197+
```yaml
198+
gos_web_socket:
199+
authentication:
200+
enable_authenticator: true
201+
providers:
202+
session:
203+
firewall: ~ # This should match the `gos_web_socket.client.firewall` config value
204+
session_handler: '@session.handler.pdo' # This should match the `gos_web_socket.client.session_handler` config value
205+
```
206+
207+
If you are using the client storage with a Symfony cache decorator, you can migrate this configuration to the new storage by moving your config values from the `client` section to the `authentication` section
208+
209+
Example Legacy Configuration:
210+
211+
```yaml
212+
gos_web_socket:
213+
client:
214+
storage:
215+
driver: 'cache.websocket'
216+
decorator: 'gos_web_socket.client.driver.symfony_cache'
217+
```
218+
219+
Example New Configuration:
220+
221+
```yaml
222+
gos_web_socket:
223+
authentication:
224+
storage:
225+
type: psr_cache
226+
pool: 'cache.websocket'
227+
```
228+
229+
2) Replace class and service references
230+
231+
The new authentication API is a full replacement for the legacy implementation. When enabled, the legacy implementation cannot be used.
232+
233+
The below table shows an approximate map of the legacy interfaces to the new interfaces alongside their purposes:
234+
235+
| Legacy API | New API | Purpose |
236+
| --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
237+
| `Gos\Bundle\WebSocketBundle\Client\Auth\WebsocketAuthenticationProviderInterface` | `Gos\Bundle\WebSocketBundle\Authentication\AuthenticatorInterface` | Authenticates the given connection to the server |
238+
| `Gos\Bundle\WebSocketBundle\Client\ClientConnection` | `Gos\Bundle\WebSocketBundle\Authentication\TokenConnection` | DTO holding the connection and security token |
239+
| `Gos\Bundle\WebSocketBundle\Client\ClientManipulatorInterface` | `Gos\Bundle\WebSocketBundle\Authentication\ConnectionRepositoryInterface` | Queries the connection storage to find connections and tokens |
240+
| `Gos\Bundle\WebSocketBundle\Client\ClientStorageInterface` | `Gos\Bundle\WebSocketBundle\Authentication\Storage\TokenStorageInterface` | Manages the token storage for the websocket server |
241+
| `Gos\Bundle\WebSocketBundle\Client\Driver\DriverInterface` | `Gos\Bundle\WebSocketBundle\Authentication\Storage\Driver\StorageDriverInterface` | Storage layer for security tokens for the websocket server (typically only implemented for custom storage drivers) |
242+
243+
The below table shows an approximate map of the legacy service IDs to the new service IDs:
244+
245+
| Legacy Service | New Service | Purpose |
246+
| --------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------- |
247+
| `gos_web_socket.client.authentication.websocket_provider` | `gos_web_socket.authentication.authenticator` | Authenticates the given connection to the server |
248+
| `gos_web_socket.client.manipulator` | `gos_web_socket.authentication.connection_repository` | Queries the connection storage to find connections and tokens |
249+
| `gos_web_socket.client.storage` | `gos_web_socket.authentication.token_storage` | Manages the token storage for the websocket server |

0 commit comments

Comments
 (0)