RTC: Allow local users to collaborate via PingHub using blog-token auth#47835
Open
RTC: Allow local users to collaborate via PingHub using blog-token auth#47835
Conversation
Local users (no Jetpack user token) hitting the pinghub-token endpoint were getting a generic 500. Now: - PHP: detect missing user token early and return 403 user_not_connected instead of making a doomed WPCOM API call. - Bridge: on user_not_connected, fire wpcom-rtc-user-not-connected on window and block all further connect() attempts to avoid an infinite reconnection loop. - gutenberg-rtc-notices: new RtcUserNotConnectedModal listens for that event and shows a modal with a "Link account with WordPress.com" CTA (using the authorization URL passed from PHP via wpcomRtcNotices config). - Tests: new REST_Pinghub_Token_Test covers the 403 path and the WP_Error passthrough in create_item(). Fixes https://linear.app/a8c/issue/DOTCOM-16476 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
|
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.
Interested in more tips and information?
|
Contributor
|
Thank you for your PR! When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:
This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖 Follow this PR Review Process:
If you have questions about anything, reach out in #jetpack-developers for guidance! |
Allowing users to dismiss the modal and edit locally risks data loss when WS-based RTC is active. Remove the X button and click-outside behaviour, and replace the dismiss link with "Back to posts". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Code Coverage SummaryCoverage changed in 2 files.
|
…cated context get_authorization_url() crashes when no user is logged in because it tries to read user properties from the integer 0 returned by get_current_user_id(). Add a user_id guard so the URL is only generated for actually-logged-in users who have not yet linked their account. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phan doesn't evaluate function_exists() guards, so it sees the stub definition in tests/php/bootstrap.php as a redefinition of the real function (which is included via parse_file_list from jetpack-mu-wpcom). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…enerating connect URL get_authorization_url() internally queries for site creation date in a way that crashes in WorDBless test environments. Semantically, a user-connect URL only makes sense when the site itself is Jetpack-connected (has blog token), so adding is_connected() as a prerequisite is both correct and test-safe. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nnected errors When the PingHub JWT endpoint returns user_not_connected, both our new RtcUserNotConnectedModal and Gutenberg's built-in SyncConnectionErrorModal were appearing simultaneously. Add a module-level event listener that sets userNotConnectedReceived, and register an editor.SyncConnectionErrorModal filter (always-on, unlike the limit-notices filter) that returns null when that flag is set, letting our modal handle the prompt exclusively. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ng a modal Instead of loading the PingHub provider and showing a modal when the JWT fetch fails, exclude pinghub from the default providers list when the current user lacks a WP.com connection. This lets Gutenberg's built-in HTTP-polling handle RTC transparently — local users can edit alongside connected (WS) users with slightly higher latency but no interruption. Reverts all client-side user_not_connected handling (bridge flag, window event, modal, SyncConnectionErrorModal filter, connectUserUrl config) since the PingHub provider is never instantiated for these users. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…are state The sync.providers filter was replacing existing providers with PingHub, which killed Gutenberg's built-in HTTP-polling. Change to appending so connected users run both transports. The shared client-side Yjs doc bridges them: polling updates flow to PingHub and vice versa. This lets unconnected users (http-polling only) collaborate with connected users (http-polling + WS) through the common polling channel. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When getProviders() returns empty (no jetpackRTC config), return [] to disable RTC entirely instead of passing through existing providers. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of falling back to HTTP-polling for unconnected users (which has separate server-side Yjs state from WebSocket), use the site's blog token to request PingHub JWTs on behalf of local users. The WPCOM endpoint embeds a site-user identity in the JWT so PingHub can authenticate them. This means all users get the same WebSocket transport — no transport split, no modal, no conditional logic. Changes: - class-rest-pinghub-token.php: use wpcom_json_api_request_as_blog() for unconnected users, passing local_user_id in the request body - class-rtc.php: remove http-polling fallback for unconnected users; all users now get pinghub as the default provider - rtc.ts: replace default providers instead of appending, since all users use PingHub - Tests updated to reflect new behavior Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ows-local-editing-without-enabling-collaboration
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.
Fixes DOTCOM-16476
Proposed changes
Local users (accounts not linked to WordPress.com) were unable to use real-time collaboration because PingHub JWT tokens could only be issued to connected users. Previously, the token endpoint returned a 403
user_not_connectederror, causing a "Connection lost" modal in the editor.Instead of falling back to HTTP-polling (which has separate server-side Yjs state from WebSocket and cannot bridge changes between transports), this PR enables local users to authenticate with PingHub via the site's blog token.
The Jetpack site requests a JWT on behalf of the local user using
wpcom_json_api_request_as_blog(), passing the local user's identity in the request body. The WP.com endpoint issues a JWT with asite-user:<blogId>:<localUserId>subject claim, which PingHub's auth layer recognizes and trusts (since the blog token already proves the request comes from the authenticated site, which verifiededit_postscapability before requesting the token).This means all users get the same WebSocket transport — no transport split, no modal, no conditional logic.
Jetpack-side changes (this PR)
class-rest-pinghub-token.php: For connected users, unchanged (wpcom_json_api_request_as_user). For unconnected users, useswpcom_json_api_request_as_blog()withlocal_user_idin the request body.class-rtc.php: Removed thehttp-pollingfallback for unconnected users. All users now getpinghubas the default provider.rtc.ts: Replaces default providers (instead of appending) since all users use PingHub.WP.com-side changes (separate commit)
jetpack-pinghub.php: Accepts blog-token auth (is_jetpack_authorized_for_site()). For blog-token requests, readslocal_user_idfrom the body and issues a JWT withsub: "site-user:<blogId>:<localUserId>".pinghub-auth.php: Recognizessite-user:sub format in JWT validation. Returns a negative sentinel ID that never collides with real WP.com user IDs.pinghub-auth-rtc-helper.php: For site-user sentinels, skipsis_user_member_of_blog()and trusts that the Jetpack site verified capabilities. Verifies the blog_id in the sentinel matches the room's blog_id.Related product discussion/links
Does this pull request change what data or activity we track or use?
No.
Testing instructions
wpcom-features-edgesticker to your site.wp-sync/v1/updates).