Next Phenomenal Microbrewery

    @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>
    

    Install

    npm i @formidable-webview/web

    DownloadsWeekly Downloads

    7

    Version

    0.0.1

    License

    MIT

    Unpacked Size

    187 kB

    Total Files

    72

    Last publish

    Collaborators

    • jsamr