@formidable-webview/web
TypeScript icon, indicating that this package has built-in type declarations

0.0.1 • Public • Published

@formidable-webview/web

A security-aware WebView component for react-native-web aimed at feature parity.

Setup

Install

npm

npm add @formidable-webview/web

yarn

yarn add @formidable-webview/web

Configure Webpack

You just need to add a new line to map react-native-webview in your webpack config script (webpack.config.js):

config.resolve.alias['react-native-webview'] = '@formidable-webview/web';

Features

Disclaimer

This library uses <iframe> elements to render remote resources and reproduce the WebView component behavior on web pages. This comes with limitations you should be aware of.

Most notably, some features will only work on same origins. This limitation is enforced by web browsers. Check the Origin column for each feature. When a prop or method is marked with “Same Origin”, it means that one of the following conditions must be met for this feature to work:

  • Using source={{ html }} prop;

  • Using source={{ uri }} prop and the uri domain is equal to the page domain;

  • Using source={{ uri }} prop and the uri domain shares a superdomain with the page domain and both pages declare the same superdomain with document.domain.

You are strongly advised to read the Security Notes chapter to learn more.

Shared Props

This is a support overview for WebViewSharedProps in this library.

Behavior Props

Prop Support Origin Comments
javaScriptEnabled ✔️ Any When sandboxEnabled is false, JavaScript will always be enabled.
containerStyle ✔️ Any Default styling is guaranteed strictly equivalent with community WebView for mobile platforms.
style ✔️ Any Default styling is guaranteed strictly equivalent with community WebView for mobile platforms.
renderError ✔️ Any Because HTTP requests from the browser cannot be accessed for obvious security reasons, and onerror events are not triggered on iframes in modern browsers, we use a little trick: we send an opaque HTTP HEAD request to the resource, and catch an error when the host is unavailable. You can disable this feature by setting errorRenderingEnabled to false.
renderLoading ✔️ Any
mediaPlaybackRequiresUserAction ✔️ Any
showsHorizontalScrollIndicator ✔️ Any
showsVerticalScrollIndicator ✔️ Any
source ⚠️ Any Both remote URI (with the exception of body, headers and method) and inline HTML (including baseUrl) are supported. Local files are not supported.
injectedJavaScript ⚠️ Same origin
injectedJavaScriptBeforeContentLoaded ⚠️ Same origin
originWhitelist ⚠️ Same origin Note that contrary to mobile platforms, this prop will default to [], because it makes little sense to allow navigation within the iframe.
injectedJavaScriptForMainFrameOnly Any Consider the behavior of web as if this prop was forced to true.
injectedJavaScriptBeforeContentLoadedForMainFrameOnly Any Consider the behavior of Ersatz as if this prop was forced to true.
nativeConfig None
userAgent None
applicationNameForUserAgent None
allowsFullscreenVideo None Use allowsFullscreen instead.
cacheEnabled None
javaScriptCanOpenWindowsAutomatically None Popups opened with window.open will be suppressed by modern browsers.
startInLoadingState None

Event Handlers Props

Event Handler Support Origin Comments
onScroll ✔️ Any
onLoad ✔️ Any Invoked when the WebView has finished the load operation with success.
onLoadEnd ✔️ Any Invoked when the WebView has finished the load operation, either with a success or failure
onError ✔️ Any Invoked when the WebView has finished the load operation with a failure.
onLoadStart ✔️ Any Invoked when the WebView is starting to load from a source object.
onLoadProgress ✔️ Any Although we support this, only one event will be fired at the end with progress: 1.
onMessage ⚠️ Same Origin Invoked when a script in the backend has posted a message with window.ReactNativeWebView.postMessage.
onNavigationStateChange ⚠️ Same Origin Navigation events from a cross origin will not be tracked.
onShouldStartLoadWithRequest ⚠️ Same Origin Navigation events from a cross origin will not be cancelable.
onHttpError None There is no way to access HTTP requests submitted by browsers.
onFileDownload None

Web-only Props

Prop Type Default Origin Comments

csp

string

undefined

Any

Set iframe csp attribute.

referrerPolicy

string

undefined

Any

Set iframe referrerpolicy attribute.

geolocationEnabled

boolean

false

Any

Sets whether Geolocation API can be used.

allowsFullscreen

boolean

true

Any

Sets whether Fullscreen API can be used.

allowsPayment

boolean

true

Any

Sets whether PaymentRequest API can be used.

allowsPreserveOrigin

boolean

true

Any

Sets whether the embedded browsing context preserves its own origin. Setting this prop to false will assign this browsing context an opaque origin. It will have great security benefits, at the cost of limited features. When false, any prop that has the "same origin" limitation will be ignored.
Remarks: Under the hook, this prop maps to sandbox="allow-same-origin" iframe attribute.

lazyLoadingEnabled

boolean

false

Any

Set iframe loading="lazy". This feature has the potential to boost page loading performances and limit memory consumption, but is yet experimental.

sandboxEnabled

boolean

true

Any

By default, the iframe will be sandboxed for safety. You can disable this behavior by setting this prop to true. This is highly discouraged and can lead to security vulnerabilities. You are advised to whitelist features and permissions you need with webPolicies prop instead. Read more about the security risks associated with removing sandboxing here.

messagingEnabled

boolean

true

Any

Sets whether WebView messaging is enabled.

webPolicies

{ [k in string]: boolean | string }

Variable (depends on other props)

Any

A map to override iframe allow and sandbox attributes to set permission policies. If you need access to specific peripherals, it can be allowed here (microphone, camera, battery …​).

Read our detailed guide: In-depth Review of prop.

Instance Methods

For any of the unsupported methods, a method is defined but will do nothing when invoked.

Method Support Origin Comments
requestFocus ✔️ Any
injectJavaScript ⚠️ Same Origin Document is not accessible in cross-origins iframes.
reload ⚠️ Any Reload works, but navigation history will be lost.
goBack None Navigation is not supported.
goForward None Navigation is not supported.
stopLoading None Method is present but does nothing.

Security Notes

Iframes have been an attack vector and security breach for a long time. Nowadays, iframes feature new attributes to protect the embedding page from attacks.

By default, the IframeWebView component will sandbox the underlying iframe to limit attack surface. You are encouraged to review the sandbox attribute by reading this article: www.html5rocks.com/en/tutorials/security/sandboxed-iframes/. You’ll be able to use webPolicies prop to grant specific sandbox permissions. See In-depth Review of prop.

Same Origin Policy

Because of the same origin policy, iframes will be rendered in a restricted environment when the origin of the WebView doesn’t match the origin of the current page. In such restricted environments, the current page will not have access to the content of the cross origin page, and thus many features will be affected, among which:

  • JavaScript injection will be disabled;

  • Messaging will be disabled;

These restriction do not apply to inline HTML. If you are in control of the cross origin and this cross origin is a subdomain of this page or vice versa, you can set an explicit superdomain in the subdomain page(s) to work around this issue:

document.domain = "company.com";

Read more about this on MDN.

Also note that when allowsPreserveOrigin prop is set to false, the embedded browsing context will have a unique opaque origin, meaning it won’t share its origin with the embedding page, nor with itself. Under the hood, this prop maps to sandbox="allow-same-origin" attribute when true. Disabling the same origin is probably the safest approach, especially when the embedding page shares its origin with the embedded, but it comes with great limitations.

IFrame Attributes

You are encouraged to use props mapped to iframe attributes to address security concerns in iframes:

Iframe Attribute IframeWebView Props Security Gain
allow webPolicies Configure which web APIs are available in the embedded page and to which origins, such as payments, peripherals…​ Read more about permissions policies here.
csp csp Enforce the embedded browsing context to limit the range of origins from which external resources can be loaded.
referrerpolicy referrerPolicy Instruct which referrer the browser should attach with HTTP requests sent to embedded pages hosts.
sandbox sandboxEnabled, webPolicies, allowsPreserveOrigin Whitelist embedded page permissions (javascript, forms…​) and allow or deny the page to preserve its own origin.

Content Security Policy

If you are using CSP directives, you should make sure the domain rendered in the WebView is whitelisted. For example, the most specific directive for embedding youtube player would be:

Content-Security-Policy: frame-src https://*.youtube.com;

If no frame-src directives is set, user agents will fallback to, by order of preference, child-src and default-src directives. Read more on MDN.

In-depth Review of webPolicies prop

webPolicies prop is a map to override iframe allow and sandbox attributes to set permission policies. Keys of this map are the camelCased translation of the following items:

  • Browser features;

  • Sandbox features.

The value for each key can either be:

  • true, which will enable the permission with no allowlist (defaults to *);

  • false, which will disable the permission by setting allowlist to 'none';

  • a string, which should follow the allowlist syntax to specify origins.

Some policies will be derived from specific props such as allowsFullscreen. Policies from webPolicies will be merged into policies derived from props, meaning you can override derived policies from props with webPolicies. It is however best advised to favor the most specific props when available, as other iframe attributes might be set as a result for retro-compatibility.

Sandbox Features

Some webPolicies relate to iframe sandbox attribute. When such policies are set, the corresponding rules will be mapped to both allow and sandbox iframe attributes, to follow W3C proposed standard while still being retro-compatible. The only exception is allow-same-origin, which will be determined by allowsPreserveOrigin prop. An exhaustive sandbox features list is maintained by W3C and available here.

The below component

const webPolicies = {
  forms: "https://*.other-domain.com",
};

function MyComponent() {
  return (
    <IframeWebView
      allowsPreserveOrigin
      javaScriptEnabled
      webPolicies={webPolicies}
      source={{ uri: "https://domain.com/" }}
    />
  );
}

will be rendered as

<iframe
  src="https://domain.com/"
  allow="scripts; forms https://*.other-domain.com"
  sandbox="allow-same-origin allow-scripts allow-forms"
></iframe>

You will notice a few things:

  • scripts rules are derived from javaScriptEnabled prop;

  • allow-same-origin sandbox rule is derived from allowsPreserveOrigin prop;

  • The forms web policy is mapped to both sandbox and allow, but the latest is more restrictive: it only allows forms on subdomains of other-domain.com with https protocol. As per the proposed standard, the most restrictive rule should be enforced if the web browser supports policy-controlled sandbox features.

Browser Features

Browser features includes, among other things:

  • Data-sensitive APIs such as Camera, Microphone and other sensors;

  • Payment and Fullscreen APIs;

  • Outdated APIs such as synchronous XHR;

  • Images responsiveness enforcement.

Some features will be derived from specific props such as:

  • allowsPayment;

  • allowsFullscreen;

  • mediaPlaybackRequiresUserAction;

  • geolocationEnabled.

These props will map to any of the corresponding web features. An exhaustive features list is maintained by W3C and available here.

The below component

const webPolicies = {
  accelerometer: "https://domain.cdn.com",
  camera: false,
  // Don't do this; this policy is derived from allowsFullscreen prop.
  fullscreen: false,
  pictureInPicture: true
};

function MyComponent() {
  return (
    <IframeWebView
      allowsPayment
      allowsFullscreen
      javaScriptEnabled
      webPolicies={webPolicies}
      source={{ uri: "https://domain.com/" }}
    />
  );
}

will be rendered as

<iframe
  src="https://domain.com/"
  allow="accelerometer https://domain.cdn.com; camera 'none'; payment; fullscreen; picture-in-picture"
  sandbox="allow-same-origin allow-scripts"
></iframe>

Dependents (0)

Package Sidebar

Install

npm i @formidable-webview/web

Weekly Downloads

38

Version

0.0.1

License

MIT

Unpacked Size

187 kB

Total Files

72

Last publish

Collaborators

  • jsamr