@dataunlocker/defender-react
TypeScript icon, indicating that this package has built-in type declarations

2.1.4 • Public • Published

DataUnlocker Defender protects your web app against network and JavaScript intrusions 🦾

💊 DataUnlocker is like a magic pill that makes all front end data collection and tracking tools work even when browsing with ad blockers, network filters, VPN etc.

In turn, DataUnlocker Defender is a controller script tightly coupled with your web application's source code, making it impossible to cut it out of the JavaScript web app.

This package is a React wrapper over the core module. Check other packages for your specific tooling:

Installation

⚠️ We highly recommend you to follow guided onboarding on admin.dataunlocker.com instead of trying to install this package manually.

DataUnlocker Defender is designed to be installed directly to your client-side JavaScript web app, like you would install any typical NPM package. However, you will need to provide DATAUNLOCKER_ID environment variable before installing this package.

export DATAUNLOCKER_ID=000000000000000000000000
npm install @dataunlocker/defender-react

You can also append the DATAUNLOCKER_ID variable to .npmrc file, located in the root of your project. You can create this file if it doesn't exist. Note that environment variables will override ones which are defined in .npmrc file.

DATAUNLOCKER_ID=000000000000000000000000
  • DATAUNLOCKER_ID
    • ID of your domain you can find in DataUnlocker Admin.
  • DATAUNLOCKER_ENDPOINT (OPTIONAL)
    • A healthy transport endpoint address without the protocol and the trailing slash.
    • For path-based endpoint, supply website.com/eXamPLe.
    • For DNS-based endpoint, eXamPLe.website.com.
    • For custom endpoints, custom.site.com/optional-path.
    • If not provided, the package picks the latest (healthy) transport endpoint for the domain when being installed. For instance, if you have 2 healthy endpoints, the most recently created one will be picked up. If you have none, the installation will fail.

You will be guided at DataUnlocker Admin about further installation steps.

⚠️ Before you continue

DataUnlocker installation is always considered "clean" (working even when ad blockers are on) when you install it for the first time. I.e. the installation is unique to every domain and there's no feasible generic filter against it. However, as the time passes by, DataUnlocker on your web app may or may not get noticed by adblock community, so you have to properly integrate it to avoid DataUnlocker being "voided" over time.

DataUnlocker Defender checks that your DataUnlocker setup works properly within your web app, on every page load. Simply put, it tells you if something is trying to block or bypass DataUnlocker via the corresponding handlers. It is important to program your web app in a way that if this check fails, your web app reacts to it (for example, you can stop interactivity in your web app, stop sending or receiving data, prevent rendering, navigation etc). This is a core paradigm of DataUnlocker self-defense mechanism. No worries: the tracking haters community won't intentionally release a filter to break your web app, as users will start disabling ad blockers and complain on inability to circumvent DataUnlocker.

❗️ If you don’t properly handle the Defender check's “error“ and “load“ in your web app, the blockers will be able to circumvent DataUnlocker and void it over time.

A few more things to note:

  • Mind to embed DataUnlocker Defender's code right into your JavaScript sources (modern JavaScript bundlers do it by default).
    • If DataUnlocker Defender is served as a separate JavaScript resource on a predictable URL, it can be blocked easily.
  • Mind to avoid letting the web app to run (or be interactive) if DataUnlocker setup was reported with "error".
    • Just rendering warning popups or overlays can be easily removed by content blockers.
    • Using document.body.innerHTML = '...' can be easily defused too (JavaScript rules in uBlock, Brave etc).
    • Instead, modify the business logic of your web app. Set the scoped (non-window) variables, prevent API calls, navigation etc - these things cannot be "patched" by extensions.

JavaScript API

Defender check will pass for 100% of your users (including those using ad blockers), but it has to be preprogrammed in a specific way to ensure nothing (like ad blocker) can "cut" DataUnlocker from your web app.

DataUnlocker Defender's API for React is very straightforward:

import { Defender } from '@dataunlocker/defender-react';
import { FC, ReactNode, useEffect } from 'react';

// Your application's root node.
const Root: FC<{ children: ReactNode }> = ({ children }) => {
  return (
    // <Defender> handles all states: onLoad and onError. You can also pass them as props.
    <Defender>{children}</Defender>
  );
};

<Defender> component influences (a bit) how your web app is rendered, on purpose:

  1. It inserts a wrapper <div>. You can style it by passing style or className props to Defender.
  2. Your app is rendered as-is on server-side rendering.
  3. Your app is hydrated as-is on the client.
  4. Immediately after hydration, your app becomes non-interactive (non-clickable, etc) until DataUnlocker Defender loads. It takes milliseconds. This is essential functional part of DataUnlocker's protection to prevent blockers from targeting your web app later.
  5. Once DataUnlocker Defender loads, your app becomes interactive, like it would.

These changes does not affect your SEO or page load speed ("initial paint").

Full API:

//...
return (
  /**
   * You can optionally pass onLoad and onError handlers if you want to execute some code when Defender loads/errors.
   *
   * Returning boolean {true} from onError handler will prevent displaying Dead End screen on errors, but it will still make the web app non-interactive (by design).
   */
  <Defender onLoad={_} onError={_}>
    {children}
  </Defender>
);
//...

✅ Correct integration example (1)

Use <Defender> component and DataUnlocker's secure enclave to house the tracking tools code such as Google Tag Manager (GTM).

layout.tsx:

import { FC, ReactNode } from 'react';
import { Defender } from '@dataunlocker/defender-react';

const Layout: FC<{ children: ReactNode }> = ({ children }) => {
  // ✅ Handles all Defender states.
  return <Defender>{children}</Defender>;
};

// ✅ Tracking tools are configured in "DataUnlocker's secure enclave" in DataUnlocker Admin,
// and will be injected by DataUnlocker automatically.

export default Layout;

✅ Correct integration example (2)

Alternatively if you prefer

layout.tsx:

import { FC, ReactNode } from 'react';
import { Defender } from '@dataunlocker/defender-react';

// Your tracking initialization function. Example: GTM.
// ⚠️ This code is still vulnerable to ad blockers. You can try to obfuscate it or better use DataUnlocker's secure enclave.
const initTracking = () => {
  const dataLayerVariableName = Date.now().toString(36).slice(-5); // Make DataLayer variable name random to avoid JS blocking
  const _window = window as any;
  const dataLayer = (_window[dataLayerVariableName] =
    _window[dataLayerVariableName] || []);
  dataLayer.push(['js', new Date()]);
  dataLayer.push(['config', 'G-279DATA']);
  const script = document.createElement('script');
  script.src = `https://www.googletagmanager.com/gtag/js?id=G-AAAA&l=${dataLayerVariableName}`;
  document.body.appendChild(script);
};

const Layout: FC<{ children: ReactNode }> = ({ children }) => {
  // ✅ Handles all Defender states. If you need custom handling, implement it with `useDefender` hook only.
  return <Defender onLoad={initTracking}>{children}</Defender>;
};

export default Layout;

✅ Make sure to minify and tree shake your application's javascript bundle.

✅ Correct integration example (3)

You can also use useDefender hook if you want more customization.

Mind this when using useDefender hook:

  • When writing manual implementation, mind that ad blockers have tools to replace any code string or influence browser APIs.
  • Defender may report error at any time (after or before the load event). error is set once when DataUnlocker setup is detected as broken or circumvented.
  • Without handling both isLoading and error it will be easy for ad blockers to block DataUnlocker.
    • Don't let your web app to be interactive until isLoading is false;
    • Don't let your web app to be interactive if there is an error; optionally, notify the user about the error (included in <Defender/>).
  • The effect of handling load/error should be observable immediately, in a sense that anyone who contributes to adblock filter lists won't be clicking through your entire web app after finding a set of rules that circumvent DataUnlocker.
  • In case DataUnlocker detects ad blocker at a later stage (after load was emitted), a page refresh will happen along with an error event. This is an intended behavior.
  • When actually integrating DataUnlocker to production, you may want to first skip implementing handlers and observe that no issues arise using console.log.

layout.tsx:

import { FC, ReactNode } from 'react';
import { useDefender, getPlaceholder } from '@dataunlocker/defender-react';

/** Your tracking initialization. Use DataUnlocker's Secure Enclave instead if possible. */
const initTracking = () => {
  // Make DataLayer variable name random to avoid simple JS blocking
  const dataLayerVariableName = 'dataLayer';
  const _window = window as any;
  const dataLayer = (_window[dataLayerVariableName] =
    _window[dataLayerVariableName] || []);
  dataLayer.push(['js', new Date()]);
  dataLayer.push(['config', 'G-279DATA']);
  const script = document.createElement('script');
  script.src = `https://www.googletagmanager.com/gtag/js?id=G-AAAA&l=${dataLayerVariableName}`;
  document.body.appendChild(script);
};

const Layout: FC<{ children: ReactNode }> = ({ children }) => {
  const [isLoading, defenderError] = useDefender();

  useEffect(() => {
    if (!isLoading) {
      // Init tracking after Defender loads on the client
      initTracking();
    }
  }, [isLoading]);

  // ✅ Handle 3 states manually (without using <Defender/> component):
  // 1. Defender reports an error: display this error to the user. This protects your DataUnlocker installation.
  // 2. Defender is not yet loaded: make your application non-interactive or display the loading state.
  // 3. Defender has no errors and is loaded: render your application.
  return defenderError ? (
    <div dangerouslySetInnerHTML={getPlaceholder().innerHTML} />
  ) : isLoading ? (
    <div>The app is loading, please wait...</div>
  ) : (
    /* Your app's code */
    children
  );
};

export default Layout;

❌ Incorrect integration example (1)

Handle both isLoading and error, which is equally and crucially important.

layout.tsx:

import { FC, ReactNode, useEffect } from 'react';
import {
  Defender,
  useDefender,
  getPlaceholder,
} from '@dataunlocker/defender-react';

const initTracking = () => {
  // Your tracking initialization function.
};

const Layout: FC<{ children: ReactNode }> = ({ children }) => {
  const [isLoading, defenderError] = useDefender();

  useEffect(() => {
    if (!isLoading) {
      initTracking();
    }
  }, [isLoading]);

  // ❌ Missing loading state handling. Defender may never load nor error if influenced by blockers.
  return defenderError ? (
    <div dangerouslySetInnerHTML={getPlaceholder().innerHTML} />
  ) : (
    /* Your app's existing code */
    children
  );
};

export default Layout;

FAQ

How do I ensure Defender won't break my web app one day?

Defender retrieves a validation code with cryptographic signature from DataUnlocker's servers per each web app page load, which helps it ensure that the connection and the setup is genuine. Without getting a signed response from DataUnlocker servers for whatever reason, Defender will report an interference (onError).

Consider the following to mitigate risks:

  • Add both dev and prod websites to DataUnlocker Admin as separate domains, and ensure their setup is identical.
  • In DataUnlocker Admin UI, you can always manually turn Defender off in case when something went wrong and you need some time to debug. When disabled, Defender will load immediately and never error.
  • Make sure your project on DataUnlocker is paid. You have a requests overuse allowance to cover spikes in traffic, but running out of traffic for continuous period of time will invalidate your installation and always fire onError.
  • Make sure your reverse proxy is up. If it goes down, Defender's check will fail too.

Facts about Defender:

  • To minimize network issues, DataUnlocker Defender retries a failed network "probe" request just once.
  • DataUnlocker proxy is built on Cloudflare, which guarantees the highest uptime. If you're using Cloudflare as a proxy to DataUnlocker, you're safe.

Contributing

DataUnlocker's team is open to accept any suggestions or bug reports. Feel free to reach out to our support.

Package Sidebar

Install

npm i @dataunlocker/defender-react

Weekly Downloads

15

Version

2.1.4

License

none

Unpacked Size

21.6 kB

Total Files

7

Last publish

Collaborators

  • dataunlocker