---
title: "Check that requests come from your app · Agent Wranglers"
url: https://agentwranglers.se/playbook/mobile-apps/data-on-the-device/app-attestation
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Check that requests come from your app

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Data on the device](https://agentwranglers.se/playbook/mobile-apps/data-on-the-device.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

- ID: MOB-21
- Level: Recommended
- Chapter: [Data on the device](https://agentwranglers.se/playbook/mobile-apps/data-on-the-device)
- Rule: Send an attestation token from App Attest or Play Integrity with calls that cost money or unlock content. The server refuses calls without a valid token.
- How: A script can copy your API calls and skip the app. App Attest on iOS and Play Integrity on Android let the server check that a call came from your unmodified app. A service such as Firebase App Check wraps both. Attestation proves the app, not the person, so keep [enforce permissions on the server](https://agentwranglers.se/playbook/app-ui/permissions-flags-and-failures/server-enforces-permissions). Not all devices support App Attest, so decide what an unsupported device may do. Start in a mode that only logs failures, then enforce once the failure share is known.
- Check: A call to a protected endpoint from a script, with a valid session but no attestation token, gets 401. The same call from the release app succeeds.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### TypeScript example

```ts
import { getAppCheck } from 'firebase-admin/app-check';

app.use('/api/coach', async (req, res, next) => {
  const token = req.header('X-Firebase-AppCheck');
  if (!token) return res.status(401).end();
  try { await getAppCheck().verifyToken(token); next(); } catch { res.status(401).end(); }
});
```

### Python example

```python
from firebase_admin import app_check

@app.before_request
def require_app_check():
    token = flask.request.headers.get("X-Firebase-AppCheck", default="")
    try:
        app_check.verify_token(token)
    except Exception:
        flask.abort(401)
```

## Sources

- [Apple, Establishing your app's integrity](https://developer.apple.com/documentation/devicecheck/establishing-your-app-s-integrity)
- [Firebase App Check](https://firebase.google.com/docs/app-check)

## Related rules

- [Enforce permissions on the server](https://agentwranglers.se/playbook/app-ui/permissions-flags-and-failures/server-enforces-permissions) in App interface

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/mobile-apps/data-on-the-device/app-attestation">Agent Wranglers mobile apps playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Ship no secrets in the app package](https://agentwranglers.se/playbook/mobile-apps/data-on-the-device/no-secrets-in-app)
- Next: [Keep secrets out of device backups](https://agentwranglers.se/playbook/mobile-apps/data-on-the-device/backup-exclusions)
- [Data on the device](https://agentwranglers.se/playbook/mobile-apps/data-on-the-device)
