App attestation
App attestation proves to your backend that a request comes from an unmodified build of your app running on a genuine device that meets your policy. Included in every TrustPin plan at no extra charge.
This page is for security leads and everyone starting with attestation: what it proves, who runs what, and the path to a first verified token.
It is not runtime protection (RASP): it doesn’t detect hooking, debugging, tampering, root or jailbreak. See What attestation is not.
Who runs what
TrustPin operates no attestation service. Everything in the request path runs in your infrastructure. TrustPin’s only runtime role is serving the signed policy (attest.jws.b64) from its CDN.
| Component | Runs at | Role |
|---|---|---|
| TrustPin console and CLI | TrustPin, your workstation | Set up, edit, sign and publish the attestation policy; generate the deployment bundle |
| TrustPin CDN | TrustPin | Serves the signed policy to your verifier |
| Attestation verifier | Your infrastructure (container image or binary) | Verifies Apple App Attest, Android Key Attestation and Play Integrity evidence against the policy; issues short-lived tokens |
| SDK attestation module | Your app | Produces evidence, obtains and renews tokens, adds the TrustPin-Attestation header to requests |
| Your API or gateway | Your infrastructure | Verifies tokens against the verifier’s JWKS and decides what unattested requests may do |
TrustPin stores identifiers only: Team IDs, bundle IDs, package names, signing-certificate digests, the Google Cloud project number. It never holds your verifier’s signing keys, challenge secrets or Google credentials; those are environment variables of your verifier.
What TrustPin sees
TrustPin sees no attestation traffic. From your verifiers’ policy fetches it records:
- request counts, split into full fetches and “not modified” answers;
- verifier versions and the operating system and architecture they run on;
- the CDN location that served the fetch;
- counts of distinct verifier server IP addresses.
None of this is device or end-user data. You see it on the project’s Analytics page (“Attestation verifiers”), the Usage pages (“Attestation policy fetches”) and the Status tab of the project’s Attestation page (“Verifier fleet”).
The flow
Platform evidence is expensive (an Apple or Google round trip, subject to quotas), so it is produced once per device key. Tokens are then renewed cheaply by signing a fresh challenge with that key.
1. Attest (once per device key)
app ── GET /v1/challenge ───────────────────────▶ verifier
app ── POST /v1/attest {clientData, device key, evidence} ▶ verifier ── Apple / Google
app ◀─ { keyCertificate, token } ───────────────────
2. Refresh (before the token expires, or when the binding changes)
app ── GET /v1/challenge ───────────────────────▶ verifier
app ── POST /v1/token {keyCertificate, clientData, assertion} ▶ verifier
app ◀─ { token } ───────────────────────────────────
3. Use
app ── API request + TrustPin-Attestation: <token> ▶ your API (verifies via JWKS)The device key is the App Attest key on Apple platforms (Secure Enclave) and an Android Keystore key on Android. A key is attested exactly once; when its key certificate expires or is rejected, the SDK generates a new key and attests again. The Key certificate lifetime in the policy (default 30 days) therefore controls how often a device attests again.
For an operation your API must not accept on a reused token alone, SDK 7.1.0 and later can add an assertion bound to that operation. See Assertions for sensitive operations.
Token binding
A token can be bound to your user’s session, so a token copied out of one session is useless in another. Only a SHA-256 of the value leaves the device, and your API compares it with the session the request authenticates with.
The app chooses one of two modes: header binding, where each request’s token is bound to a header such as Authorization, or fixed binding, to a value you set. With Require token binding on, tokens exist only once the app has a binding.
Modes
| Mode | Verifier behaviour | Use |
|---|---|---|
| Monitor | Issues a token even when policy checks fail, recording tp.result fail and tp.reasons. An app the policy doesn’t list gets 403 app_not_allowed in both modes | Roll-out: your API logs tp.result and allows every request |
| Enforce | Issues tokens only when the policy passes. Tokens issued before a switch to enforce keep fail until they expire; see Rolling out | Production: your API rejects requests without a valid token |
Cryptographic failures (forged evidence, invalid signatures) never yield a token in either mode.
Platforms
SDK 7.1.0 and later.
| Platform | Evidence | Minimum OS |
|---|---|---|
| iOS, iPadOS | App Attest | 15 |
| macOS, Mac Catalyst | App Attest | 13, 15 |
| tvOS | App Attest | 15 |
| watchOS | App Attest | 9 |
| visionOS | App Attest | 2 |
| Android with Google Play services | Play Integrity, Key Attestation or both | API 25 |
| Android without Google Play | Key Attestation | API 25 |
| Simulators and emulators | Debug token (development only) | Not applicable |
Where a device cannot produce the evidence the policy requires, the SDK reports it and sends requests without the header; your API decides how to treat them.
Setting it up
- Apps and policy. On the project’s Attestation page, add your apps (Team ID and Bundle ID on Apple; Package name and Signing certificate SHA-256 on Android), choose the mode and token settings, then Sign and publish. Running verifiers pick up a new version within their refresh interval (5 minutes by default). See Setting up apps and the policy.
- Verifier. Deploy it from the console’s deployment bundle. See Deploying the verifier.
- Pinning. Register the verifier’s domain in your TrustPin project, because the SDKs pin their calls to it. See Setting up apps and the policy.
- App. Add the attestation module and point it at your verifier. See iOS and macOS or Android. Flutter and React Native are coming.
- API. Verify the
TrustPin-Attestationheader. See Verifying tokens in your API or Checking tokens at the gateway. - Roll out. Measure in monitor mode, then enforce. See Rolling out.
Projects signed with FIDO can’t use app attestation in this release.