Make WordPress Core

Opened 15 months ago

Last modified 14 months ago

#58120 new feature request

oEmbed Mastodon

Reported by: mediaformat's profile mediaformat Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Embeds Keywords:
Focuses: Cc:

Description

Porting this issue here for discussion.

If I put a link to a Mastodon toot in the embed block, it embeds, but it has no style applied and looks bad:

https://user-images.githubusercontent.com/1994311/230915699-45480012-7db9-4eff-93cd-f64dd4e46a09.png

As I mentioned there Since Mastodon is not 1 single website, but software that can be deployed to any url, we might need a different approach than the provider whitelist.

Attachments (1)

230915699-45480012-7db9-4eff-93cd-f64dd4e46a09.png (212.5 KB) - added by Shelob9 15 months ago.
This is a screenshot of a toot added to my site using the embed block

Download all attachments as: .zip

Change History (10)

@Shelob9
15 months ago

This is a screenshot of a toot added to my site using the embed block

#1 @Shelob9
15 months ago

A challenge here is Mastodon isn't a website, it's federated. This is a similar problem to a WordPress embed -- could be WordPress.com could be one of millions of other sites. Mastodon and by extension ActivityPub uses Webfinger to identify users "WebFinger as described in RFC 7033 is a spec that defines a method for resolving links to a resource, given only a URI on a particular server." from: https://docs.joinmastodon.org/spec/webfinger/

If there was a Mastodon embed block, when a Toot is added, the URL for a toot has the server URL for the Mastdon instance in it. WordPress could use that URL to identify the user and get the toot via the API https://docs.joinmastodon.org/methods/statuses/#get

#2 @johnbillion
15 months ago

This appears to be a CORS problem, but I'm not yet sure why.

What's happening:

  1. User wants to embed a toot (this link in the example from @mediaformat) so pastes it into the editor, either directly or via the Embed block.
  2. Mastodon supports oEmbed auto-discovery, therefore that URL contains an application/json+oembed link that points to an oEmbed endpoint: https://mastodon.social/api/oembed?format=json&url=https%3A%2F%2Fmastodon.social%2F%40jk%2F110169910775357223 .
  3. The html property in the oEmbed endpoint response includes an <iframe> and a <script> tag. The iframe is allowed by the oEmbed handler in WordPress and is outputted. The script tag isn't and therefore gets stripped but it doesn't appear to matter because it's only there to adjust the height of the contents of the iframe and doesn't affect the main styling.
  4. When the <iframe> is displayed on the page a CORS restriction somewhere prevents its CSS and JS files from loading -- this is the actual cause of the missing styles -- but I've not figured out the details yet. CORS doesn't normally restrict CSS and JS files from loading within a third party iframe.

Needs some more investigation.

Last edited 15 months ago by johnbillion (previous) (diff)

#3 @Shelob9
15 months ago

@johnbillion Thanks for testing, you are correct. I am testing here: https://pluginmachine.com/test-maston-embed/ and I am getting CORS errors like Acess to script at 'https://mastodon.social/packs/js/common-0173e2f60b9cdf71cbe2.js' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

My testing and other folks testing shows this is issue with Mastodon, not one Mastodon instance.

Possibly related issues in Mastodon:

#4 @Otto42
15 months ago

The problem is indeed on Mastodon's side, and it's due to their use of the crossorigin attribute.

Example taken from here: https://pluginmachine.com/test-maston-embed/

The actual embedded iframe is this one.
<iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted" src="https://mastodon.social/@Josh412/110191480902863091/embed#?secret=Axzy6A59eJ" data-secret="Axzy6A59eJ" width="500" height="750"></iframe>

The actual content of that iframe looks like this for various scripts and style sheets.
<link rel="stylesheet" media="all" crossorigin="anonymous" href="/packs/css/common-a844dc34.css" integrity="sha256-P7jsS35wKfKZ7egDM6otdXk7d8JjmsE5i9R8SD1vD8s=">

That crossorigin="anonymous" attribute is forcing this request to be a CORS request. Which is indeed understandable...

The problem is that the actual serving of the CSS or JS file does not contain the 'Access-Control-Allow-Origin' header On those files responses from the server. Whether the files are static or generated by Mastodon is irrelevant because that is a required header when you force the request to be a CORS request.

If they removed that attribute entirely and left it out then possibly that request would work. However, leaving it there or empty is forcing the issue to make it a CORS request. Therefore, Mastodon needs to control the output of the headers such that all files are served with the proper headers.

#5 @peterwilsoncc
15 months ago

For oembed iframes inserted via auto-discovery, WordPress adds the attributes sandbox="allow-scripts" security="restricted".

In my testing, removing the sandbox attribute from the iframe causes the Mastodon oembeds to work: scripts and styles load as expected.

Unfortunately WordPress can't remove the attribute from embeds permitted via auto-discovery for security reasons.

An exception is made for sites on the oembed allow-list (YouTube, Twitter, etc) but due to the nature of of Mastodon, it's impractical for WP to add each instance to the allow-lost. WordPress would need to review the embed JavaScript for each instance to ensure that nasty-hackers dot social hasn't modified the code from the default.

As such I think this needs to be resolved upstream by mastodon:

  • ensure the embeds work with only the attributes in the WP html embed allow list (see wp_filter_oembed_result()
  • use the mastodon embed.js file to enhance the embed's iframe for admin users using the full embed code

I've created an upstream ticket in the hope the Mastodon folks can help address this, see mastodon/mastodon#24534

#6 follow-up: @Otto42
15 months ago

Note that adding allow-same-origin to the sandbox will also make the styling happen correctly.

Also note that to test this properly, you need to clear (or disable) the oembed cache in the postmeta. The iframe, including the sandbox field, is stored as post meta for caching reasons.

The reason this works is because, without the ability to allow-same-origin, the contents of the iframe pass their origin as null. Therefore, the CORS checks don't pass because the origin doesn't match.

I am uncertain of the security implications for this. Nevertheless, it is a viable option rather than eliminating sandbox security entirely.

#7 in reply to: ↑ 6 ; follow-up: @peterwilsoncc
15 months ago

Replying to Otto42:

Note that adding allow-same-origin to the sandbox will also make the styling happen correctly.

...snip...

I am uncertain of the security implications for this. Nevertheless, it is a viable option rather than eliminating sandbox security entirely.

It's quite unwise, I am afraid. Per the notes on MDN's iframe page:

When the embedded document has the same origin as the embedding page, it is strongly discouraged to use both allow-scripts and allow-same-origin, as that lets the embedded document remove the sandbox attribute — making it no more secure than not using the sandbox attribute at all.

#8 in reply to: ↑ 7 @Otto42
15 months ago

Replying to peterwilsoncc:

When the embedded document has the same origin as the embedding page,

This is never really going to be the case for oembeds, though.

#9 @triumvirate
14 months ago

Thanks for submitting this, @mediaformat ! Long live the Mastodon federation.

Wow, this is something else. I learned/ am learning quite a bit just attempting to parse all of this.

WordPress would need to review the embed JavaScript for each instance to ensure that nasty-hackers dot social hasn't modified the code from the default.

Thinking perhaps too outside the box here, could WordPress itself host embed.js, and substitute its own version whenever it detects a Mastodon one attempting to be used?

However, on second thought, as @johnbillion mentions

The script tag isn't and therefore gets stripped but it doesn't appear to matter because it's only there to adjust the height of the contents of the iframe and doesn't affect the main styling.

It seems that would be insufficient as this script isn't actually necessary, anyway?

But maybe the principle still applies? Self-host whatever js/ css Mastodon sites are to inject?

Note: See TracTickets for help on using tickets.