If you’re trying to access the camera and microphone with getUserMedia() in a cross origin iframe on a recent version of Chrome, by default it will fail.

We’ve encountered this situation several times as users of the Pipe audio and video recording platform tried to embed Pipe in Wix websites or Google Site which use iframes to embed external HTML and JS code.

The cause stems from a series of security & privacy changes that were made to Chrome in 2017 and 2018:

  1. Chrome 60 introduced Feature Policy which gave devs a way to control usage of sensitive features inside their websites
  2. Chrome 64 blocked camera and microphone access in cross origin iframes by default and required Feature Policy to grant access

Feature Policy allows you to control what sensitive APIs and features are available to the website in the browser. You can control whether or not these features are available through the Feature-Policy HTTP header OR by using the allow attribute in HTML iframes.

Sensitive features now include:

  • accessing the camera and microphone through getUserMedia()
  • the fullscreen API
  • the geolocation API
  • accessing the accelerometer or USB devices

You can check Chrome's source for an up to date list of features that are under Feature Policy control or chromestatus.com for a list of policies that are considered for implementation.

Below, we will be focusing on the allow attribute in iframes as it relates to camera and microphone access through getUserMedia(), for a more detailed overview of Feature Policy check out this excellent introduction article.

The allow attribute of the iframe HTML element enables you to control the sensitive features available within that iframe. The syntax is quite simple, it has the following form:

<iframe src="" allow="feature_name allow_list"></iframe>

You can specify allow lists for more than one feature by using semicolons:

<iframe src="" allow="feature_name allow_list;feature_name allow_list"></iframe>

Thus, to allow camera and microphone access in a cross origin iframe you need to add the following allow attribute to your iframe:

<!--Allow camera and microphone access within the context of this iframe-->
<iframe src="https://example.com" allow="camera;microphone"></iframe>

which is equivalent to:

<!--Allow camera and microphone access within the context of this iframe-->
<iframe src="https://example.com" allow="camera *;microphone *"></iframe>

The above will allow any page, hosted on any domain to request access to the camera and microphone of the user while loaded through the above iframe.

To tighten things up you can be more granular about which domains have access to those features as the allow_list can have any of the following values:

  • *: used above, the feature is allowed in top-level browsing contexts and in nested contexts (iframes)
  • 'self': the feature is  allowed in top-level browsing contexts and same-origin nested contexts. The feature is not allowed in cross-origin documents for nested browsing contexts.
  • 'none': The feature is not allowed at all in top-level and nested browsing contexts.
  • <origin(s)>: The feature is allowed in specific origin(s), for example https://my_website.com

Without the iframe allow attribute above, camera and microphone access won't be allowed in cross origin iframes in browsers that have Feature Policy implemented (see browser support below). If you're listening for getUserMedia() errors you'll get a NotAllowedError because requests blocked by Feature Policy fail in a similar way as it would if a user had denied a permission prompt.

This is the reason why our own Pipe recorder cannot get camera and microphone access in Chrome when embedded in a website that is built with Wix. Wix adds any embedded content through a cross origin iframe which does not have the required allow attribute:

The Wix iframe code used for embeds lacks Feature Policy's allow attribute

Until the release of Feature Policy, the camera and microphone API was already controlled by an older attribute named allowusermedia, but this attribute is now deprecated and you should use the new allow attribute with the correct values instead, in order to control the access.

If both the old attribute and the new attributes are specified the more restrictive policy will take precedence (in Chrome). For example, the following iframe would not be allowed to request access to the camera, because allow="camera 'none'" is more restrictive than allowusermedia:

<iframe allowusermedia allow="camera 'none'" src="https://example.com"></iframe>

It is very important to remember that iframes inherit the feature policy settings of their parent page, this means that if both the page and iframe specify a feature policy list, the more restrictive policy will apply. So, if features in your iframe still do not work, make sure there's not a more restrictive policy imposed through HTTP headers on the parent page.

Browser support

Chrome 60 introduced Feature Policy support for both the HTTP header and the allow attribute and from our tests, other Chromium based browsers also support the feature like Opera and the new Edge based on Chromium.

For a full list of browsers that support the feature see caniuse.com.

Chrome 74 also introduced a JavaScript API that allows you to detect which features are allowed by an iframe, page or browser. You can access the API with frame.featurePolicy for iframes or document.featurePolicy for the page/browser/main document. More details can be found here.

Further reading: