Skip to main content
Account sharing has a recognizable shape: one account, many devices, sometimes many countries in a short window. ShieldLabs gives you two layers to see it, and your own code decides the policy (a paid seat is fine, a credential resold to fifty people is not).

Layer 1: device and geo per login

On every authenticated session, the webhook carries the DeviceID and Country for that account’s UserHID. Compare them against the account’s known devices in real time.

Layer 2: Abuse Patterns over time

Server-side Abuse Patterns that link an account across sessions: “Many Devices on One Account”, “Multiple Countries on One Account”, “New Device and New Country on One Account”. Read on the dashboard.
ShieldLabs links and surfaces. It never blocks anyone. Your code reads the device and country spread, then decides: allow, require re-authentication on the new device, enforce a concurrent-session limit, or flag the account for review.
Account sharing is a policy question, not a fraud verdict. A family plan, a shared team login, and a resold credential can all look like “many devices on one account.” ShieldLabs tells you the spread; your terms of service decide what is allowed.

How the two layers work together

LayerWhat it answersWhere you read itLatency
Per-session device/geo”Is this account on a new device or in a new country right now?”Webhook / History APIReal time (~1s)
Abuse Patterns”Has this account spread across many devices or countries over time?”Dashboard Patterns + exportBackground (~10 min)

Layer 1: check the device on every authenticated session

Call checkAuthenticatedUser with the account’s hashed id (UserHID) on login and on sensitive actions. The webhook then carries the DeviceID, Country, and UserHID for that session, so your backend can compare against what it already knows about the account.
app.html
<script type="module">
  const mod = await import(
    'https://cdn.shieldlabs.ai/snippet.js?publicKey=YOUR_PUBLIC_KEY'
  );
  // Pass the hashed account id, never a raw email or user id.
  mod.checkAuthenticatedUser('8a9f-hashed-account-id', (serverResponse, requestID) => {
    document.getElementById('shield-request-id').value = requestID;
  });
</script>
Your backend reads the scored result for that RequestID from the webhook cache (or the History API), then checks the DeviceID against the account’s known devices.
api/session-check.js
app.post('/api/session-check', async (req, res) => {
  const { accountId, shieldRequestId } = req.body;

  // Pull the ShieldLabs result for this session.
  const shield = await waitForScore(shieldRequestId, 2000);
  const deviceId = shield?.DeviceID;
  const country  = shield?.Country;

  // Compare against what you already know about this account.
  const known = await knownDevicesFor(accountId);   // your own store

  if (deviceId && !known.devices.has(deviceId)) {
    // A device this account has never used. Your call: re-auth, notify, or just log.
    if (known.devices.size >= YOUR_DEVICE_LIMIT) {
      return res.json({ action: 'reauth_required', reason: 'new_device_over_limit' });
    }
    await rememberDevice(accountId, deviceId, country);
    return res.json({ action: 'notify_new_device' });
  }

  return res.json({ action: 'allow' });
});
DeviceID is browser-bound. The same person on a second browser (Chrome then Safari) produces a different DeviceID and looks like a new device, because there is no cross-browser recognition today. So “many devices” can include one person’s own browsers. Weigh it with the country spread and your own context before you treat it as sharing. See Identifiers.

Layer 2: see the spread with Abuse Patterns

A single new device is easy to read. The harder signal is an account that quietly appears on a dozen devices, or from several countries within an hour. That is what Abuse Patterns surface, grading each account Normal → Suspicious → Dangerous as the linked count crosses thresholds in a rolling window.
One account used from many different devices. The core account-sharing and account-resale shape. The grouping identity is the UserHID; the spread is counted in distinct DeviceIDs over a rolling window (default 30 days).
The same account active from several countries in a short window (24 hours). Catches a credential shared across regions, or one used behind rotating VPN exits. Note that a real traveler can trip this, so read it with the device spread.
An existing account suddenly appears from a device and a country it has never used together. A strong account-takeover or hand-off signal, distinct from steady sharing.
You read these on the dashboard Patterns tab, where each account shows its grade and the identifiers behind it. Levels never downgrade: once an account is flagged Dangerous, new clean activity does not clear it.

Reconstruct an account’s spread programmatically

You can also compute the spread yourself from the History API. Search by user_hid and count the distinct devices and countries.
Read one account's history
curl "https://api.shieldlabs.ai/{domain}:{secret}/history/user_hid/8a9f-hashed-account-id?limit=100"
Count devices and countries per account
async function accountSpread(userHid) {
  const rows = await shieldHistory('user_hid', userHid, 100);
  const devices   = new Set(rows.map((r) => r.DeviceID).filter(Boolean));
  const countries = new Set(rows.map((r) => r.Country).filter(Boolean));
  return { devices: devices.size, countries: countries.size };
}

// Enforce your own policy.
const { devices, countries } = await accountSpread('8a9f-hashed-account-id');
if (devices >= YOUR_DEVICE_LIMIT || countries >= YOUR_COUNTRY_LIMIT) {
  flagForReview(accountId, { devices, countries });
}
The History API bills 1 request per returned row (an empty result still bills 1). For routine enforcement, prefer the pre-computed pattern export from the dashboard as your watchlist, and reserve live user_hid reads for the accounts you are actively investigating.

Putting it together

1

Identify authenticated sessions

Call checkAuthenticatedUser with the hashed UserHID on login and sensitive actions. See Snippet setup.
2

Track known devices per account

On each webhook, record the DeviceID and Country against the account in your own store, so you can tell a new device from a familiar one.
3

Act on a new device (Layer 1)

Allow a known device. For a new one over your limit, require re-authentication or notify the account owner. Your policy, your thresholds.
4

Watch the patterns (Layer 2)

Pull “Many Devices on One Account” and “Multiple Countries on One Account” from the dashboard and review the Dangerous accounts.
5

Tune to your product

A streaming service tolerates more devices than a single-seat B2B tool. Start in logging-only mode, see how your real accounts distribute, then set limits that match your terms.
A guide, not a rule. The right device and country limits depend entirely on your product.
SignalSuggested action
Known device, known countryAllow
New device, within your device limitNotify the account owner, remember the device
New device, over your device limitRequire re-authentication on the new device
”Multiple Countries on One Account” (Suspicious or Dangerous)Step up verification, review against your sharing policy
”New Device and New Country” (Dangerous)Treat as possible takeover: force re-auth, see Login and 2FA

Next: Acting on the Risk Score

The full decision playbook, including how to combine identity spread with the per-session Risk Score and its Details.