Repository navigation
feat(realtime): let extensions authorize their own private channels - #5220
Open
ernestdefoe wants to merge 1 commit into
Open
ernestdefoe wants to merge 1 commit into
ernestdefoe wants to merge 1 commit into
Conversation
Add `Extend\Realtime::authorizePrivateChannel($subject, $callback)`, so an
extension can open `private-{subject}={id}` and decide who may subscribe.
Until now the auth endpoint only accepted channels with a method on
AuthController, so an extension's channel always got a 403, and anything
scoped to one model had to go through PHP and `private-user={id}`.
Guards fail closed: access needs at least one guard and every guard must
return exactly true. Realtime's own subjects can't be registered, so an
extension can't widen access to them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes proposed in this pull request:
This adds
Extend\Realtime::authorizePrivateChannel($subject, $callback), so an extension can open a private channel of its own,private-{subject}={id}, and decide who may subscribe to it.Today
AuthControlleronly authorizes a private channel when it has a method named after the subject (user,typing,privateMessageTypingand so on), so a channel defined by an extension always gets a 403.authorizePresenceChannel()can only narrow a presence channel realtime already defines. So an extension that needs a channel scoped to one model (a post being edited together, a call, a game) has no channel to use. Every message has to go through PHP and then out to each member'sprivate-user={id}, which costs a request per message and rules outclient-events between peers.Once subscribed, the existing relay already handles
client-events on any private channel (Message::isAuthorizedClientEvent()), and the server can push withPusher::trigger(). The only missing piece was authorization.Reviewers should focus on:
true(not just truthy).AuthControllerstill checks its own methods first, and the extender throws if a subject matches one of them.method_exists()ignores case, so that coversUseras well asuser, and also the controller's other methods (handle,online). The extender also rejects subjects the channel name format can't carry (letters only).int. Whether guests are allowed is the callback's decision, as it is for the built-intypingchannel.PrivateChannelAuthorizermirrorsPresenceChannelAuthorizer: a singleton filled throughafterResolving.Screenshot
N/A
Necessity
private-user={id}from PHP. That works, but it costs a PHP request per message. OverridingAuthControllerfrom an extension isn't an option either, because two extensions would collide.)Confirmed
yarn testinjs/). (No frontend changes.)composer test). Unit 93/93 and integration 66/66 (1 skipped) on SQLite; PHPStan clean on the changed files.PrivateChannelAuthorizerTest, new cases inRealtimeExtenderTest, andPrivateChannelExtenderAuthTestthrough/api/websocket/auth.)🤖 Generated with Claude Code