Skip to content

Add hooks for consumer-supplied connection authentication and authorization - #921

Merged
mcottontensor merged 1 commit into
EpicGames:masterfrom
mcottontensor:feature/connection-auth-hooks
Jun 25, 2026
Merged

Add hooks for consumer-supplied connection authentication and authorization#921
mcottontensor merged 1 commit into
EpicGames:masterfrom
mcottontensor:feature/connection-auth-hooks

Conversation

@mcottontensor

Copy link
Copy Markdown
Collaborator

Relevant components:

  • Signalling server
  • Common library
  • Frontend library
  • Frontend UI library
  • Matchmaker
  • Platform scripts
  • SFU

Problem statement:

The signalling server intentionally ships no authentication — consumers are expected to bring their own, appropriate to their environment. Today that is harder than it should be: there is no documented place to authenticate a connection, no way to recover an authenticated identity on a connection after the upgrade, and no way to control the id a streamer registers as. The last point also means a connection can claim a streamer id before the legitimate streamer connects (id squatting), after which the legitimate streamer is silently renamed.

This PR adds policy-free extension points so a deployment can plug in its own auth without forking. It does not add any authentication scheme, credentials, or login flow.

Solution

Three seams, all opt-in and backwards compatible:

  • Authenticate at the WebSocket upgrade (existing, now documented). streamerWsOptions / playerWsOptions / sfuWsOptions are already passed through to the underlying ws server, so a consumer can supply a verifyClient callback to accept/reject a connection before the server sends its config message (which carries peer/TURN options). This was undocumented; it is now described as the recommended authentication point.
  • Expose the HTTP upgrade request on connections (IStreamer.request / IPlayer.request). Anything a verifyClient (or other front door) attaches to the request — e.g. a resolved identity — is recoverable later for authorization decisions.
  • IServerConfig.authorizeStreamerId — an optional hook consulted in StreamerRegistry when a streamer identifies itself. It receives the requesting streamer (with its request), the requestedId, the sanitizedId the registry would otherwise commit, and whether the requested id collided. It returns the id to commit (accept the default, or override to e.g. namespace per tenant), or null to reject and disconnect the streamer. This is the seam for anti-squatting / id-ownership policy.

When neither new option is supplied, behaviour is unchanged (requested id accepted, numeric suffix appended on collision).

Changes

  • Signalling/src/StreamerRegistry.ts: add IStreamerIdAuthRequest and StreamerIdAuthorizer types; optional authorizer applied in onEndpointId (accept / override / reject, with a uniqueness guard on overrides).
  • Signalling/src/SignallingServer.ts: add authorizeStreamerId to IServerConfig, pass it to the registry, and thread the upgrade request into the three connection constructors.
  • Signalling/src/{StreamerConnection,PlayerConnection,SFUConnection}.ts and the IStreamer / IPlayer interfaces: expose optional request.
  • Docs/Security-Guidelines.md: new "Authenticating and authorizing connections" section with examples.
  • Changeset: minor bump for lib-pixelstreamingsignalling-ue5.7.

Documentation

Added a "Authenticating and authorizing connections" section to Docs/Security-Guidelines.md covering the verifyClient seam, recovering identity via connection.request, and the authorizeStreamerId hook, each with a short example. The guidance is explicit that the project ships no auth and these are seams for consumer policy.

Test Plan and Compatibility

  • npm run build (CJS + ESM) and npm run lint pass for the Signalling package; new types are present in the generated dist/types declarations.
  • Verified with a local harness driving a real SignallingServer playing the consumer-policy role:
    • Authenticate at upgrade + identity propagation + id authorization: a verifyClient that requires a token and stamps a tenant on info.req, plus an authorizeStreamerId that namespaces the id. A streamer with a valid token connected and its id was committed as tenantA:cam1 (confirmed via endpointIdConfirm), proving the request-borne identity reached the authorizer.
    • Rejection at upgrade: a streamer with an invalid token was rejected during the HTTP upgrade and never opened.
    • Default behaviour unchanged: a second server with no hooks committed the plain requested id cam1.

This complements, but does not depend on, #920 (which enforces subscriber-scoped message routing). They touch different concerns and can be reviewed independently.

The signalling server ships no authentication by design; consumers are
expected to bring their own. Add seams so they can do that without forking:

- Expose the HTTP upgrade request on connections (IStreamer.request /
  IPlayer.request) so an identity attached during a verifyClient check
  survives to later authorization decisions.
- Add IServerConfig.authorizeStreamerId, consulted in StreamerRegistry when a
  streamer identifies itself. It can accept the default id, override it (e.g.
  namespace per tenant), or reject and disconnect the streamer. This is the
  seam for preventing streamer-id squatting.

Default behaviour is unchanged when neither is supplied. The existing
*WsOptions.verifyClient pass-through (for authenticating the upgrade) and both
new seams are documented in Docs/Security-Guidelines.md.
@mcottontensor
mcottontensor marked this pull request as ready for review June 24, 2026 04:51
@mcottontensor mcottontensor added auto-backport Used to specify we want a PR to auto backport to a branch, must be paired with auto-backport-to-UEX. auto-backport-to-UE5.5 auto-backport-to-UE5.6 auto-backport-to-UE5.7 labels Jun 25, 2026
@mcottontensor
mcottontensor merged commit 8385df9 into EpicGames:master Jun 25, 2026
8 checks passed
@mcottontensor
mcottontensor deleted the feature/connection-auth-hooks branch June 25, 2026 23:53
@mcottontensor

Copy link
Copy Markdown
Collaborator Author

💔 All backports failed

Status Branch Result
UE5.5 Backport failed because of merge conflicts
UE5.6 Backport failed because of merge conflicts
UE5.7 Backport failed because of merge conflicts

Manual backport

To create the backport manually run:

backport --pr 921

Questions ?

Please refer to the Backport tool documentation and see the Github Action logs for details

@mcottontensor

Copy link
Copy Markdown
Collaborator Author

💚 All backports created successfully

Status Branch Result
UE5.7

Note: Successful backport PRs will be merged automatically after passing CI.

Questions ?

Please refer to the Backport tool documentation

mcottontensor added a commit that referenced this pull request Jun 26, 2026
…) (#930)

The signalling server ships no authentication by design; consumers are
expected to bring their own. Add seams so they can do that without forking:

- Expose the HTTP upgrade request on connections (IStreamer.request /
  IPlayer.request) so an identity attached during a verifyClient check
  survives to later authorization decisions.
- Add IServerConfig.authorizeStreamerId, consulted in StreamerRegistry when a
  streamer identifies itself. It can accept the default id, override it (e.g.
  namespace per tenant), or reject and disconnect the streamer. This is the
  seam for preventing streamer-id squatting.

Default behaviour is unchanged when neither is supplied. The existing
*WsOptions.verifyClient pass-through (for authenticating the upgrade) and both
new seams are documented in Docs/Security-Guidelines.md.

(cherry picked from commit 8385df9)

# Conflicts:
#	Signalling/src/SignallingServer.ts
mcottontensor added a commit that referenced this pull request Aug 12, 2026
The signalling server ships no authentication by design; consumers are
expected to bring their own. Add seams so they can do that without forking:

- Expose the HTTP upgrade request on connections (IStreamer.request /
  IPlayer.request) so an identity attached during a verifyClient check
  survives to later authorization decisions.
- Add IServerConfig.authorizeStreamerId, consulted in StreamerRegistry when a
  streamer identifies itself. It can accept the default id, override it (e.g.
  namespace per tenant), or reject and disconnect the streamer. This is the
  seam for preventing streamer-id squatting.

Default behaviour is unchanged when neither is supplied. The existing
*WsOptions.verifyClient pass-through (for authenticating the upgrade) and both
new seams are documented in Docs/Security-Guidelines.md.

(cherry picked from commit 8385df9)
mcottontensor added a commit that referenced this pull request Aug 12, 2026
…) (#976)

The signalling server ships no authentication by design; consumers are
expected to bring their own. Add seams so they can do that without forking:

- Expose the HTTP upgrade request on connections (IStreamer.request /
  IPlayer.request) so an identity attached during a verifyClient check
  survives to later authorization decisions.
- Add IServerConfig.authorizeStreamerId, consulted in StreamerRegistry when a
  streamer identifies itself. It can accept the default id, override it (e.g.
  namespace per tenant), or reject and disconnect the streamer. This is the
  seam for preventing streamer-id squatting.

Default behaviour is unchanged when neither is supplied. The existing
*WsOptions.verifyClient pass-through (for authenticating the upgrade) and both
new seams are documented in Docs/Security-Guidelines.md.

(cherry picked from commit 8385df9)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auto-backport Used to specify we want a PR to auto backport to a branch, must be paired with auto-backport-to-UEX. auto-backport-to-UE5.5 auto-backport-to-UE5.6 auto-backport-to-UE5.7

Development

Successfully merging this pull request may close these issues.

1 participant