Device Attestation API
Server-verifiable, hardware-backed device attestation via the opt-in package
react-native-nitro-device-integrity.
- Android → Play Integrity API
- iOS → App Attest (
DCAppAttestService) + DeviceCheck (DCDevice)
This is a separate, opt-in package from the core library. Install it only if you need real attestation; it pulls in Play Integrity / App Attest dependencies and requires Google Cloud / Apple console setup.
This API complements the core Device Integrity API.
- Local detection (
isDeviceCompromised()) — instant, offline, easily bypassed. A first-line pre-filter. - Attestation (this page) — network-bound, server-verified, strong. The authoritative signal.
Use the local check as a fast pre-filter and attestation as the gate your server trusts.
The responsibility boundary
Every method returns an opaque token. The library cannot decode it and never returns a boolean "is this device safe?" — that decision belongs to your server. You must send the token to your backend for verification.
Installation
Requirements: iOS 14.0+ (App Attest) / 11.0+ (DeviceCheck), Android API 23+ with Google Play Services.
API Reference
Availability
isSupported
Best-effort check. On Android, true when Google Play Services is available
(does not guarantee the Play Integrity API is onboarded for your app). On
iOS, DCAppAttestService.shared.isSupported — always false on the Simulator.
providerType
Which provider backs this device. Branch your client logic on this before calling platform-specific methods.
Android — Play Integrity
prepareStandardProvider()
Warms up the Play Integrity Standard token provider. Call once per session;
the provider is cached natively. cloudProjectNumber is passed as a string to
avoid JS number-precision loss.
Rejects with (in the error message): CLOUD_PROJECT_NUMBER_IS_INVALID,
PLAY_STORE_NOT_FOUND, NETWORK_ERROR, etc. On iOS: UNSUPPORTED_PLATFORM.
requestIntegrityToken()
Requests a Standard token. Requires a prior prepareStandardProvider. If the
cached provider has expired (INTEGRITY_TOKEN_PROVIDER_INVALID), it is
re-prepared once automatically and the request retried.
requestHash is a base64 SHA-256 of your request's critical parameters
(max 500 bytes) — not a server nonce; the caller computes it. Returns an
opaque, encrypted token string.
requestClassicIntegrityToken()
Play Integrity Classic flow (one-off, server-nonce based). Prefer Standard
for frequent checks. nonce must be a server-generated, single-use, URL-safe
base64 value of 16–500 bytes.
iOS — App Attest
generateKey()
Generates an App Attest key pair in the Secure Enclave and returns its keyId.
The keyId is the only handle to this key — store it (e.g. Keychain). The
library is stateless. App Attest keys do not survive app reinstall;
regenerate on DCError.invalidKey.
attestKey()
Attests a key (once per key, per install). Makes a network call to Apple.
clientDataHash is base64 of SHA-256(server challenge) — the library does
not hash for you. Returns base64 of the opaque CBOR attestation object.
generateAssertion()
Generates an assertion for a subsequent request (offline). Returns base64 of the opaque CBOR assertion object. Your server checks its monotonic counter to detect replays.
iOS — DeviceCheck
getDeviceCheckToken()
Generates an Apple DeviceCheck token (device-level, lighter than App Attest). Your server queries/updates the device's 2 bits of state via Apple's DeviceCheck API.
Usage
Server verification (your responsibility)
This library stops at issuing the token. Your backend verifies it.
Play Integrity (Android)
Send the token to your server, then call Google's decode endpoint (recommended):
Interpret the decoded verdict:
deviceIntegrity.deviceRecognitionVerdictcontainingMEETS_DEVICE_INTEGRITYappIntegrity.appRecognitionVerdict === 'PLAY_RECOGNIZED'- An empty
deviceRecognitionVerdictsignals a compromised/emulated device.
App Attest (iOS)
- Validate the attestation once per key against Apple's App Attest Root CA (cert chain, nonce, app-ID hash, counter = 0). Store the public key + counter.
- For each assertion, verify its signature with the stored public key and that the counter strictly increased.
See Validating apps that connect to your server.
Setup requirements
Android (Play Integrity)
- Create/select a Google Cloud project and enable the Play Integrity API.
- Link it in Play Console → Play Integrity API.
- Pass that Cloud project number to
prepareStandardProvider/requestClassicIntegrityToken. - On your server, set up the Google service account (or response-encryption keys).
iOS (App Attest / DeviceCheck)
- Add the App Attest capability to your app target (Xcode → Signing &
Capabilities), which adds the
com.apple.developer.devicecheck.appattest-environmententitlement. - Use a bundle identifier you own and set your Development Team.
- For DeviceCheck server queries, create a DeviceCheck
.p8key in the Apple Developer portal.
All of the above lives in your developer accounts and app target. The library cannot set it up — it only issues tokens once your app is configured.
Limitations
:::warning Be honest about what attestation can and cannot do
- App Attest is not a jailbreak detector. It proves a genuine, unmodified app on genuine Apple hardware — use it as a positive signal.
- Simulators / emulators:
isSupportedisfalseon the iOS Simulator; Play Integrity returns weak/empty verdicts on emulators. - Rooted devices: Play Integrity still returns a token, but with an empty
deviceRecognitionVerdict— your server must treat that as a failure. - Bypasses exist (e.g. PlayIntegrityFix). Attestation is one signal in a broader anti-abuse strategy, not an absolute guarantee.
- Network + console configuration required. Without them, token issuance rejects — handle the rejection; don't treat it as "safe".
- App Attest throttling: Apple rate-limits
attestKey. Control call frequency in your app; the library adds no retry/timer (it stays stateless). :::
