[{"data":1,"prerenderedAt":79},["ShallowReactive",2],{"doc:\u002Fdocs\u002Fmobile-attestation":3},{"page":4,"toc":74,"updated":78},{"path":5,"title":6,"seoTitle":7,"description":8,"blocks":9},"\u002Fdocs\u002Fmobile-attestation","Mobile app attestation","Mobile App Attestation and Device Integrity","How Fingerly checks that a mobile report comes from your genuine app on a genuine device: hardware-backed attestation on Android, and integrity signals on iOS.",[10,13,18,20,38,40,44,47,49,52,59,62],{"type":11,"text":12},"p","A mobile app talks to Fingerly from a device you do not control. Attestation and integrity signals tell you whether the report came from your genuine app on a genuine, unmodified device.",{"type":14,"level":15,"text":16,"id":17},"heading",2,"Android: hardware-backed attestation","android-hardware-backed-attestation",{"type":11,"text":19},"On Android 7.0 (API 24) and newer, the SDK uses the device's hardware-backed keystore to attest to a fresh challenge from Fingerly. The server verifies the result. There is nothing to configure.",{"type":21,"columns":22,"rows":25},"table",[23,24],"Step","What happens",[26,29,32,35],[27,28],"1","The SDK requests a [challenge](\u002Freference\u002Fattestation-challenge): 32 random bytes, valid for five minutes, usable once.",[30,31],"2","The device keystore creates a temporary key attested to that challenge, and the SDK includes the attestation in its report.",[33,34],"3","Fingerly verifies the attestation against Google's roots, checks it was made for your app and signing certificate, that the device's boot state is verified, and that the challenge is fresh and unused.",[36,37],"4","A failed check raises the `tampering` signal, and an app not signed as expected raises `cloned_app`.",{"type":11,"text":39},"Because each challenge is consumed on use, a captured report cannot be replayed.",{"type":41,"tone":42,"text":43},"callout","note","Attestation needs no Google Play services, so it also works on devices without them.",{"type":14,"level":15,"text":45,"id":46},"iOS: integrity signals","ios-integrity-signals",{"type":11,"text":48},"On iOS, device integrity comes from the SDK's own checks, scored on the server: jailbroken devices, the Simulator, attached instrumentation, intercepted traffic, runtime tampering and developer tooling. Each is a [signal](\u002Fdocs\u002Fsignals) with its own weight.",{"type":14,"level":15,"text":50,"id":51},"Using the result","using-the-result",{"type":53,"samples":54},"code",[55],{"label":56,"lang":57,"code":58},"Server","ts","async function checkIntegrity(requestId: string) {\n  const event = await fingerly.events.get(requestId)\n  const tampered = event.triggers.some((t) => t.signal === 'browser_tampering' || t.signal === 'cloned_app')\n\n  if (tampered) return refuse('app integrity')\n  return allow()\n}",{"type":41,"tone":60,"text":61},"tip","On events read with a secret key, triggers are grouped: `tampering` appears under `browser_tampering`, and `app_cloner` under `cloned_app`. See [signals](\u002Fdocs\u002Fsignals).",{"type":63,"columns":15,"cards":64},"cards",[65,70],{"title":66,"text":67,"href":68,"logo":69},"Android SDK","Install and identify.","\u002Fdocs\u002Fsdks\u002Fandroid","android",{"title":71,"text":67,"href":72,"logo":73},"iOS SDK","\u002Fdocs\u002Fsdks\u002Fios","apple",[75,76,77],{"id":17,"text":16,"level":15},{"id":46,"text":45,"level":15},{"id":51,"text":50,"level":15},"2026-09-17T16:59:11.000Z",1789667797515]