Skip to main content

Advanced SDK

noscript beacon

When JavaScript is completely disabled in the browser, the snippet never loads. The noscript beacon catches these cases using a 1×1 pixel image:
<script type="module">
  const mod = await import('https://cdn.shieldlabs.ai/snippet.js?publicKey=YOUR_PUBLIC_KEY');
  mod.checkAnonymous();
</script>
<noscript>
  <img
    src="https://rest.shieldlabs.ai/noscript?publicKey=YOUR_PUBLIC_KEY"
    width="1" height="1"
    style="display:none" alt=""
  >
</noscript>
What happens when JS is disabled:
FieldValue
Score90
Details[{"Value": 90, "Description": "JavaScript disabled"}]
UserHID"unknown"
DeviceID"unknown"
WebhookFires normally
Add img-src https://rest.shieldlabs.ai to your CSP img-src directive if you use the noscript beacon.

Hashing user IDs

Never pass raw user IDs, emails, or phone numbers to checkAuthenticatedUser(). Always hash them first.
// Browser: hash with Web Crypto API
async function hashUserId(userId) {
  const encoder = new TextEncoder();
  const data = encoder.encode(String(userId));
  const hashBuffer = await crypto.subtle.digest('SHA-256', data);
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}

const hashedId = await hashUserId(currentUser.id);
mod.checkAuthenticatedUser(hashedId);
Use the same hashing logic on your server when looking up users by UserHID from the webhook.

Force check patterns

Use forceCheck* methods at high-risk moments to get a fresh score, regardless of session cache:
// Before checkout
document.getElementById('checkout-btn').addEventListener('click', async (e) => {
  e.preventDefault();
  const mod = await import(`https://cdn.shieldlabs.ai/snippet.js?publicKey=${PUBLIC_KEY}`);
  mod.forceCheckAuthenticatedUser(userHashedId, async (ip, requestId) => {
    // Wait for webhook, or proceed and apply score async
    const response = await fetch('/api/initiate-checkout', {
      method: 'POST',
      body: JSON.stringify({ requestId }),
    });
    if (response.ok) window.location.href = '/checkout';
  });
});

SPA and route changes

In Single Page Applications, call checkAnonymous() or checkAuthenticatedUser() once on app initialization. The session cache handles deduplication — you don’t need to re-check on every route change.
// App.js — called once
import { useEffect } from 'react';

function App() {
  const { user } = useAuth();

  useEffect(() => {
    (async () => {
      const mod = await import(`https://cdn.shieldlabs.ai/snippet.js?publicKey=${PUBLIC_KEY}`);
      if (user) {
        const hashedId = await hashUserId(user.id);
        mod.checkAuthenticatedUser(hashedId);
      } else {
        mod.checkAnonymous();
      }
    })();
  }, [user?.id]); // re-check only if user changes

  return <Router />;
}

Module caching

The import() call is cached by the browser — importing the same URL multiple times returns the same module. You can safely call import() in multiple places without performance overhead.
// This is fine — browser caches the module
async function shieldCheck() {
  const mod = await import(`https://cdn.shieldlabs.ai/snippet.js?publicKey=${PUBLIC_KEY}`);
  return mod;
}