Most recipes in this Cookbook use the Risk Score to raise friction on a suspicious visit. This one runs the same machinery in the opposite direction: a visit that arrives clean and on a device you already trust is a good moment to remove friction, not add it. You can skip a redundant check, restore preferences, or smooth the path for someone who has been here before.
The two ingredients are already in every webhook: a low Risk Score and a durable DeviceID. When both point at a device you have associated with a verified account, treat the visit as a recognized return. When either is missing, fall back to your normal flow with no downside.
This recognizes a device, not a person. It is a convenience signal, not a credential, and you should read the guardrails at the end of this page before you wire it to anything sensitive.
Why the DeviceID makes this work
The DeviceID is server-derived and durable. ShieldLabs computes it from the browser itself, so it stays stable when a visitor clears cookies, opens an incognito window, or rotates their IP. A returning person on the same browser keeps the same DeviceID even after their cookie resets, which is exactly the case where a cookie-only “remember me” falls apart. The Identification reference covers how the DeviceID and VisitorID differ and which one survives what.
That durability is what lets you recognize a return without asking the visitor to log in again first. The DeviceID arrives on the webhook, or you can read it back from the History API by request_id, alongside the Risk Score for that same visit.
The pattern
Identify on the page
Load the snippet where the return matters, for example a returning visitor landing on your app or starting a routine action. Keep the requestID to join the browser check to the webhook. Receive the score and DeviceID
Verify the webhook and read the visit back by RequestID. The same Data object carries both the Score and the DeviceID you will match on.
Match a clean DeviceID to a trusted account
If the score lands in the Clean band with no anonymity signals, and the DeviceID is one you have already associated with a verified account, treat it as a recognized returning device.
Lighten the experience
Skip a redundant step, restore preferences, or shorten the path. If the device is new or the score is not clean, run your normal flow unchanged.
Step 1: associate a DeviceID with a trusted account
Recognition only works once you have something to recognize. The association is yours to build: whenever a visitor completes a real, authenticated action you trust (a login behind your own auth, a verified purchase, an email confirmation), store the DeviceID that arrived on that visit against that account.
Build the trust list on a verified action
// Call this from a flow you already trust: a successful login, a confirmed
// purchase, an email verification. The DeviceID rides in on the same webhook.
async function rememberTrustedDevice(accountId, shield) {
// Only remember devices seen on a clean, signal-free visit. Anything else
// is noise you do not want to recognize later.
if (shield.score > 9 || shield.details.length > 0) return;
await trustedDevices.add({
accountId,
deviceId: shield.deviceID,
firstSeen: Date.now(),
});
}
Over time each account accumulates a small set of devices it has genuinely used. That set is the list you match against on the next visit.
Step 2: recognize the return
On the next visit, wait briefly for the score, then check two things together: the band and the device. A clean score on its own is an ordinary visit. A known DeviceID on its own is not enough either. The recognition is the intersection.
app.post('/api/enter', async (req, res) => {
const { accountId, shieldRequestId } = req.body;
// Poll the cache, then fall back to a History API read by request_id.
const shield = await waitForScore(shieldRequestId, 2000);
// No score yet is not the same as "trusted". When in doubt, run the full flow.
if (!shield) return res.json({ recognized: false });
// A Clean band (0-9) with no anonymity signals is a low-risk, ordinary visit.
const isClean = shield.score <= 9 && shield.details.length === 0;
// Is this DeviceID one we already tied to this verified account?
const isKnownDevice = await trustedDevices.has(accountId, shield.deviceID);
if (isClean && isKnownDevice) {
// Recognized returning device: lighten the experience.
// Skip a redundant check, restore preferences, smooth the path.
return res.json({ recognized: true, deviceId: shield.deviceID });
}
// New device, or a score that is not clean: your normal flow, unchanged.
return res.json({ recognized: false });
});
waitForScore is the shared webhook-cache read (poll the cache, then fall back to a History API read by request_id); the Cookbook defines it once alongside the handler that populates the cache, so this recipe does not repeat it.
Match on the band plus the DeviceID, never the DeviceID alone. A returning device with a Medium or High score is a returning device on a riskier connection, and the riskier connection is the part that should drive your decision.
What “lighten the experience” can mean
Recognition buys you room to remove friction for a known-good return. A few safe places to spend it:
- Skip a redundant check. A second-factor prompt that the same trusted device already cleared this week can be relaxed, while staying on for anything sensitive.
- Restore preferences. Re-apply the visitor’s layout, language, or saved cart before they ask.
- Shorten the path. Pre-fill what you already know for this device, or drop a returning visitor straight onto the screen they last used.
- Soften rate limits. A recognized device earns more headroom than an anonymous one on the same endpoint.
Each of these is a convenience, and each one degrades gracefully: if recognition fails, the visitor simply gets your normal flow.
Guardrails
A returning device is a convenience signal, not a credential. State the limits plainly and design around them.
- This recognizes a device, not a person. Anyone using that browser inherits the recognition. A shared family laptop or a borrowed machine will match.
- It is probabilistic, an estimate, not proof of identity. ShieldLabs reports Accuracy as up to 99%, which is high enough to smooth a path and far too low to be the only thing standing between a stranger and an account.
- Never make it the sole gate for anything sensitive. Money movement, password and email changes, and data exports must always sit behind real authentication. Pair recognition with a login, a second factor, or a re-verification step. Use it to remove a redundant step, never to remove the only step.
- Fail closed. No score, a new device, or a non-clean band all fall back to your full flow. Recognition is the bonus, not the baseline.
Read this the way you would a “remember this device” checkbox: it makes the common case pleasant for people you already trust, and it carries no authority of its own.
Next
Tighten the friction direction with Step-up 2FA on Risky Logins, which uses the same Score and DeviceID to raise friction when a session is not clean.