Your React Native mobile-wallet app must handle rooted and jailbroken devices; would you block them outright, and how would you enforce the policy?
answer
- a block only stops honest users
- decide per action, not per app
- server enforces, client informs
- attestation for money movement
- limits, step-up, monitoring
basics
~20 sUsually not outright: a client-side block stops honest users while attackers bypass it. Enforce on the server instead, requiring verified attestation for money movement and applying limits, step-up authentication and monitoring when signals are weak.
solid answer
~50 sA blanket block looks strong but is enforced by a check the attacker controls, so it mainly locks out honest users with rooted phones or false positives. A sturdier policy is **risk-based and server-enforced**. Decide per action: browsing balances is low risk; adding a card, sending money or changing payout details is high risk. For high-risk actions, the server requires a **verified Play Integrity or App Attest result** bound to that request; when it fails or is unavailable, the server lowers limits, asks for step-up authentication or routes to review rather than trusting the app. Client root checks become one signal sent with the request, plus a warning to the user. On the device, minimise what a compromised OS could steal: short-lived tokens, no cached full card numbers, capture protection on sensitive screens. If a rule demands a hard block, enforce it server-side, where it cannot be patched away.
go deeper
Recall that a root check inside the app can be bypassed, so important decisions such as allowing a transfer are made by the server.
Explain which signals the app can collect, local root checks and attestation tokens, and how they travel with requests for the server to judge.
Propose a per-action, server-enforced policy with attestation for money movement, graceful degradation, a measured rollout and on-device exposure reduction.
Balance fraud losses, regulatory requirements and user exclusion, and own the decision of where the wallet draws the line and how it is communicated.
## Why the question is harder than it looks A **mobile wallet** stores payment credentials and moves money, so product and compliance often ask for a simple rule: "no rooted or jailbroken phones". The trouble is where that rule would run. A block implemented in the React Native app is decided by a heuristic on a device the attacker controls. Root-hiding, hooking or a patched bundle turn it off, while honest users with developer phones, some manufacturers' builds or custom ROMs are locked out. The block ends up filtering the wrong population. ## Separate the threats Rooting matters for two different reasons, and they need different controls: - **The user's own device is compromised by malware** — the risk is theft of the user's credentials and data. Controls: minimal secrets on the device, short-lived tokens, hardware-backed key storage, capture protection, warnings. - **The user is the attacker** — scripting the API, repackaging the app, faking transactions. Controls: server-side verification, attestation, limits, fraud monitoring. Neither is solved by a client-side "if rooted, exit". ## A risk-based policy | Action | Risk | Server requirement | |---|---|---| | View balance and history | low | normal session | | Add a card, change payout account | high | verified attestation plus step-up authentication | | Send money above a threshold | high | verified attestation bound to the transfer's request hash | | Any action when attestation fails | elevated | lower limits, step-up, or manual review | The server owns every decision in the right-hand column. The app's job is to collect the evidence — a Play Integrity token or App Attest assertion, obtained through a native module or Expo's alpha `@expo/app-integrity` — and send it with the request. ## Where the client still helps - **Signals.** Send the result of a local root check, such as `Device.isRootedExperimentalAsync()` in an Expo app, as one input to risk scoring, never as the decision. - **Honest warnings.** Tell users on rooted devices that the wallet's protections are weaker and why some actions need extra steps. - **Reduced exposure.** Keep no full card numbers or long-lived secrets on the device, and mask sensitive screens. ## Rolling it out 1. **Measure first.** Collect attestation verdicts and root signals for a few weeks without enforcing, to learn how many real users would be affected. 2. **Enforce on the riskiest actions**, with clear messages and a support path. 3. **Watch** false-rejection rates, support tickets and fraud losses per release. 4. **Tighten** thresholds only where fraud data justifies it. ## Edge cases the policy must survive - **Reinstalls and new phones.** App Attest keys do not survive reinstallation, device migration or restore from backup, so a returning honest user arrives with a new key. - **Unsupported environments.** App Attest is unavailable on the iOS Simulator, and QA teams often test on emulators and rooted devices; give them a test backend or allow-list rather than weakening production rules. - **Transient failures.** Integrity calls can fail on weak networks; retry once, then degrade instead of refusing. - **Shared devices.** The App Attest guidance in the Expo docs is one key per user account per device; do not reuse a key when a second user signs in on the same phone. ## Explaining it to the user A refusal the user cannot understand becomes a support ticket or a one-star review. Say what happened in plain terms ("we could not verify this device for transfers"), what still works, and what the user can do next, such as verifying their identity again or contacting support. ## When a hard block is required Some contracts or regulations require refusing compromised devices. Even then: - enforce it on the **server**, based on verified attestation, so patching the app does not bypass it; - explain the reason to the user instead of crashing or closing silently; - keep the definition narrow and documented, so support can answer "why was I blocked?". ## What interviewers look for - Recognising that a client-only block is **theatre** against attackers and friction for users. - Moving enforcement to the **server**, backed by **attestation**. - A **per-action** policy with graceful degradation. - A **measured rollout** and an honest account of what remains possible on a compromised device.
- Compliance insists the React Native wallet must refuse rooted devices. How do you meet that without a patchable client check?Make the server refuse sensitive actions unless a verified attestation shows a recognised app on a device meeting the required integrity level, and document that definition. The app only collects and forwards the evidence, then shows the server's reason. Patching the app then removes nothing, because the refusal never lived in it.
- What can still go wrong for an honest user of the wallet on a device that is compromised by malware?Malware with root privileges can read app memory, hook functions and capture the screen, so it may steal a session token or observe card details. You limit the damage with short-lived tokens, hardware-backed keys, masked screens, step-up authentication for money movement and server-side anomaly detection, not with a promise that the device is safe.
saying these in an interview costs you the question
- Exiting the app when a root check fires stops attackers.
- A passing root check on the device means the phone is safe.
- Every rooted phone belongs to an attacker.
- The React Native app should decide whether a transfer is allowed.
- Attestation failures should block users without any fallback.