# Mobile Apps

Shield scores the network behind every sensitive action in your iOS or Android
app, the same way it does for a website: VPN, proxy, Tor, hosting and
malicious networks, with a recommendation of `allow`, `monitor`, `challenge`
or `block`. Your backend confirms the verdict before it acts.

## How it works

1. Create a **mobile app** site in the Shield dashboard and register your
   app's id: the iOS bundle identifier, the Android applicationId, or both.
2. Your app calls Shield once before each sensitive action, such as a signup,
   a login, a password reset or a checkout. Shield answers with a verdict.
3. Your app sends the Shield session ID along with the request to your
   backend. Your backend asks Shield about that session with the site's secret
   key ([Server Verification](https://www.findip.net/docs/shield/server-verification.md)) and allows, challenges
   or blocks.

The site key in your app is public, like any key shipped in an app binary.
It identifies your app; it proves nothing. Only the answer your server gets
with the secret key counts.

## Three ways to connect

| Your app | What to add |
|---|---|
| Native iOS (Swift) | The `FindIPShield` Swift package, [`findip-net/findip-shield-swift`](https://github.com/findip-net/findip-shield-swift), from 1.0.0. iOS 15+, no dependencies, privacy manifest included. |
| Native Android (Kotlin) | The FindIP Shield library 1.0.0, [`findip-net/findip-shield-android`](https://github.com/findip-net/findip-shield-android): the AAR from its GitHub release until it is on Maven Central as `net.findip:shield-android`. Android 8+, `INTERNET` permission only. |
| WebView (Capacitor, Ionic, Cordova, a page in a native WebView) | The website script with `data-integration="webview"` and `data-app-id` (SDK 1.13.0 or later). Form detection and in-page enforcement work as on a website. |

The dashboard's Install page has each one with your key filled in.
Projects without a package manager can paste a single-file helper instead;
the Install page offers it under each guide.

```swift
.package(url: "https://github.com/findip-net/findip-shield-swift", from: "1.0.0")
```

```kotlin
implementation(files("libs/shield-android-1.0.0.aar"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
```

## What the app sends

| Sent | Never sent |
|---|---|
| The app id, version and build | Anything the user typed: passwords, form values, messages |
| The OS and its version | Advertising IDs (IDFA, GAID) or any cross-app identifier |
| The device family (iPhone, iPad, Pixel), never the exact model | Contacts, location, photos, or any other device data |
| Locale and timezone | A device fingerprint |
| A session ID per app launch and a visitor ID kept in the app's own storage | Plain emails or user IDs (only hashes and encrypted values, if you identify users) |
| The event name, for example `login_attempt` | |

Shield reads the IP address from the network connection, never from the app.
The full field list is in the [Events Reference](https://www.findip.net/docs/shield/events.md#mobile-app-payload).

For the App Store's App Privacy form and Google Play's Data safety form, the
SDKs collect: a device or other ID (the app-scoped visitor ID), app
interactions (the event names), and app info and performance (app version,
OS version). Purpose: fraud prevention and security. It is not used for
tracking or advertising, and it is not linked to the user's identity unless
you send identity yourself.

## Verdicts in an app

| `recommendation` | In your app | In your backend |
|---|---|---|
| `allow` | Proceed. | Proceed. |
| `monitor` | Proceed. | Proceed; log if you keep a review queue. |
| `challenge` | Proceed to your backend, which decides. | Ask for your own step-up: a one-time code, a re-login, an email link. |
| `block` | Show a neutral "could not complete" message, without a reason. | Refuse the action after verifying the session. |

No answer from Shield (no network, a timeout) means **unknown**, never safe.
Decide per action: a login can usually continue, an account signup might ask
for an extra check.

## Refusals

| Error | Meaning | Fix |
|---|---|---|
| `app_mismatch` (403) | The app id in the event is not registered on this site, or not for that platform. | Add it under Settings → Apps & keys. Debug builds with an id suffix count as separate apps. |
| `integration_not_allowed` (403) | A website sent an event with an app site's key, or an app sent one with a website site's key. | Use a website site for websites and an app site for apps. |
| `daily_quota_exceeded` (429) | The site's daily event quota is used up. | Events are accepted again from 00:00 UTC. |

The dashboard's Install page shows the last refusal with the id or page that
caused it.

## Limits

- The same Free Preview quota as a website: 10,000 events a day per site.
  Send sensitive actions, not every screen.
- One site holds up to 10 apps, so iOS, Android and staging builds of one
  product share sessions, rules and alerts.
- Device integrity checks (App Attest, Play Integrity) are not part of this
  release.
