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:
| Field | Value |
|---|
| Score | 90 |
| Details | [{"Value": 90, "Description": "JavaScript disabled"}] |
| UserHID | "unknown" |
| DeviceID | "unknown" |
| Webhook | Fires 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;
}