On this page
Short answer. Anything running on the user's device can be modified by the user. A liveness result is only trustworthy if the decision is computed on your server, from the raw capture, and tied to a single session.
The trust boundary
Your browser or mobile app runs on hardware the attacker controls. They can open developer tools, intercept network calls, patch the app and replace the camera with a video file. So any value that arrives from the client, "liveness passed: true" included, is a claim and not evidence.
How client-side verdicts get beaten
- Flag editing. Change one line of JavaScript so the verdict is always true.
- Request tampering. Intercept the call to your backend and rewrite the JSON.
- Replay. Capture one valid "passed" payload and resend it for other accounts.
- Camera substitution. Use a virtual camera so the model sees a pre-recorded or generated feed.
A client-side model can still be useful for user guidance ("move closer"), but it must never be the source of the decision.
The server-authoritative pattern
- Start a session on your backend. Create a one-time ID bound to the logged-in user, with a short expiry.
- Capture on the client and upload to your backend. Send the raw selfie, not a verdict.
- Your backend calls the verifier. The API key stays on your server; it never appears in client code.
- The verifier computes the decision and returns scores, tier and model version.
- Your backend records the result against the session and marks it used so it cannot be replayed.
// Node.js: your backend forwards the upload. The key never reaches the browser.
app.post("/kyc/selfie", upload.single("selfie"), async (req, res) => {
const session = await sessions.consume(req.body.sessionId, req.user.id); // one-time, expiring
const form = new FormData();
form.append("selfie", new Blob([req.file.buffer]), "selfie.jpg");
const r = await fetch("https://verify.skillnexus.uk/api/v1/v1/verify/basic", {
method: "POST",
headers: { "X-API-Key": process.env.IDNEXUS_API_KEY },
body: form,
});
const result = await r.json();
await results.save(session.id, result); // keep scores and decision, not the image
res.json({ decision: result.decision });
});
Checklist for a safe integration
- Sessions are single-use, expire in minutes and are bound to a user ID.
- The decision comes from a server-to-server call, never from the client.
- The API key lives in a secrets manager and is rotated.
- Results are stored with model version, tier and timestamp.
- Repeated failures from one device or IP are rate-limited.
The cost of doing it right
Server-side processing means you upload an image instead of a boolean. A resized selfie is a few hundred kilobytes, which is a small price for a decision that cannot be edited by the person being judged. Compress sensibly on the client, but do not strip the detail the model needs.
What IDNexus gives you
The decision is computed on our servers from the raw capture. There is no client-signed proof and no browser-side check to bypass. Each response carries the model version, tier and scores, which is what you need to defend a decision during an audit or a dispute.
Server-authoritative by design
Every IDNexus decision is computed server-side and returns the scores, tier and model version behind it.
Create account